Name | Type | is_array | initial_value |
acolyteBlightTumorPoint | location | Yes | |
acolyteBlightTumorUnit | unit | Yes | |
acolyteBuildingLimit | integer | No | 4 |
acolyteCitadelSound | sound | No | |
acolyteCollectionRange | real | No | 2000.00 |
acolyteDesecrateBool | boolean | Yes | |
acolyteDesecrateInt | integer | No | |
acolyteDesecratePoint | location | No | |
acolyteItemBool | boolean | Yes | |
acolyteStructureUnitGroup | group | No | |
acolyteUnit | unit | No | |
AfterDamageEvent | real | No | |
aiIsEnabled | boolean | No | |
aiPlayerIsAgent | boolean | Yes | |
aiTargetObjective | boolean | No | |
AOEDamageEvent | real | No | |
AOEDamageSource | unit | No | |
ARMOR_TYPE_ETHEREAL | integer | No | |
ARMOR_TYPE_FLESH | integer | No | |
ARMOR_TYPE_METAL | integer | No | |
ARMOR_TYPE_NONE | integer | No | |
ARMOR_TYPE_STONE | integer | No | |
ARMOR_TYPE_WOOD | integer | No | |
ArmorTypeDebugStr | string | Yes | |
ATTACK_TYPE_CHAOS | integer | No | |
ATTACK_TYPE_HERO | integer | No | |
ATTACK_TYPE_MAGIC | integer | No | |
ATTACK_TYPE_NORMAL | integer | No | |
ATTACK_TYPE_PIERCE | integer | No | |
ATTACK_TYPE_SIEGE | integer | No | |
ATTACK_TYPE_SPELLS | integer | No | |
AttackTypeDebugStr | string | Yes | |
baseDestroyedCountAlliance | integer | No | |
baseDestroyedCountHorde | integer | No | |
baseUnit | unit | Yes | |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
cryptFiendBurrowDistance | real | No | |
cryptFiendBurrowInt | integer | No | |
cryptFiendBurrowPoint | location | Yes | |
cryptFiendCocoonUnit | unit | Yes | |
cryptFiendItemBool | boolean | Yes | |
cryptFiendNerubCount | integer | No | 2 |
cryptFiendUnit | unit | No | |
DAMAGE_TYPE_ACID | integer | No | |
DAMAGE_TYPE_COLD | integer | No | |
DAMAGE_TYPE_DEATH | integer | No | |
DAMAGE_TYPE_DEFENSIVE | integer | No | |
DAMAGE_TYPE_DEMOLITION | integer | No | |
DAMAGE_TYPE_DISEASE | integer | No | |
DAMAGE_TYPE_DIVINE | integer | No | |
DAMAGE_TYPE_ENHANCED | integer | No | |
DAMAGE_TYPE_FIRE | integer | No | |
DAMAGE_TYPE_FORCE | integer | No | |
DAMAGE_TYPE_LIGHTNING | integer | No | |
DAMAGE_TYPE_MAGIC | integer | No | |
DAMAGE_TYPE_MIND | integer | No | |
DAMAGE_TYPE_NORMAL | integer | No | |
DAMAGE_TYPE_PLANT | integer | No | |
DAMAGE_TYPE_POISON | integer | No | |
DAMAGE_TYPE_SHADOW_STRIKE | integer | No | |
DAMAGE_TYPE_SLOW_POISON | integer | No | |
DAMAGE_TYPE_SONIC | integer | No | |
DAMAGE_TYPE_SPIRIT_LINK | integer | No | |
DAMAGE_TYPE_UNIVERSAL | integer | No | |
DAMAGE_TYPE_UNKNOWN | integer | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventArmorPierced | real | No | |
DamageEventArmorT | integer | No | |
DamageEventAttackT | integer | No | |
DamageEventDamageT | integer | No | |
DamageEventDefenseT | integer | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageModifierEvent | real | No | |
DamageScalingUser | real | No | |
DamageScalingWC3 | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCode | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeDebugStr | string | Yes | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypePure | integer | No | |
DamageTypePureExplosive | integer | No | |
DamageTypeReduced | integer | No | |
DEFENSE_TYPE_DIVINE | integer | No | |
DEFENSE_TYPE_FORTIFIED | integer | No | |
DEFENSE_TYPE_HEAVY | integer | No | |
DEFENSE_TYPE_HERO | integer | No | |
DEFENSE_TYPE_LIGHT | integer | No | |
DEFENSE_TYPE_MEDIUM | integer | No | |
DEFENSE_TYPE_NORMAL | integer | No | |
DEFENSE_TYPE_UNARMORED | integer | No | |
DefenseTypeDebugStr | string | Yes | |
DmgStr | string | No | |
EnhancedDamageTarget | unit | No | |
eredarWarlockDemonPoolGroup | group | No | |
eredarWarlockDemonPoolMax | integer | No | 6 |
eredarWarlockInvasionEffect | effect | No | |
eredarWarlockInvasionGateUnit | unit | No | |
eredarWarlockInvasionPoint | location | No | |
eredarWarlockInvasionTimer | timer | No | |
eredarWarlockInvasionUnit | unit | No | |
eredarWarlockItemBool | boolean | Yes | |
eredarWarlockOrbDup | boolean | No | true |
eredarWarlockTimer | timer | No | |
eredarWarlockUnit | unit | No | |
felOrcWarlockBoltTimer | integer | No | |
felOrcWarlockEradicateBonus | real | No | 1.00 |
felOrcWarlockEradicateTimer | integer | No | |
felOrcWarlockItemBool | boolean | Yes | |
felOrcWarlockSeedGroup | group | No | |
felOrcWarlockSeedTimer | integer | No | |
felOrcWarlockUnit | unit | No | |
flightPathDestinationPoint | location | Yes | |
flightPathFurthestAlliance | location | Yes | |
flightPathFurthestHorde | location | Yes | |
flightPathInFlightBoolean | boolean | Yes | |
flightPathInFlightCounter | integer | No | |
flightPathInt | integer | Yes | |
flightPathOwner | unit | Yes | |
footmanChargeCaster | unit | No | |
footmanChargeDamageBool | boolean | No | |
footmanChargeHashtable | hashtable | No | |
footmanChargeLeapers | group | No | |
footmanChargePoints | location | Yes | |
footmanChargeReal | real | Yes | |
footmanChargeTarget | unit | No | |
footmanItemBool | boolean | Yes | |
footmanShieldBashCaster | unit | No | |
footmanShieldBashCounter | integer | No | |
footmanShieldBashTimer | timer | No | |
footmanUnit | unit | No | |
forestCampBuffTimer | integer | Yes | |
forestCampClaimedGroup | group | Yes | |
forestCampFacingAngle | real | Yes | |
forestCampGroup | group | No | |
forestCampHasRespawnedRecently | boolean | Yes | true |
forestCampHiddenGroup | group | No | |
forestCampMaxInt | integer | No | |
forestCampMinimapIcon | minimapicon | Yes | |
forestCampPoint | location | No | |
forestCampRect | rect | Yes | |
forestCampShouldRespawn | boolean | Yes | false |
forestCampStoredLevel | integer | Yes | |
forestCampTempInt | integer | No | |
forestCampToggle | integer | Yes | |
forestCampUnitType | unitcode | Yes | |
forestCampX | real | Yes | |
forestCampY | real | Yes | |
gameDevMode | boolean | No | false |
gameFastStart | boolean | No | |
gameHostPlayer | player | No | |
gameInProgress | boolean | No | |
gameLoadFilter | ubersplat | No | |
gameMinionsSpawnDelay | real | No | 45.00 |
gamePlayerEmptyComp | boolean | Yes | |
gameSoundBaseDeath | sound | No | |
gameSoundNegativePing | sound | No | |
gameSoundPositivePing | sound | No | |
gameSoundStart | sound | No | |
gameSoundTutorialHint | sound | No | |
gameSoundVictory | sound | No | |
gameStartDelay | real | No | 45.00 |
gameStartShopPoint | location | Yes | |
gameStartShopPointElite | location | Yes | |
gameStartTimer | timer | No | |
gargoyleBladeDistance | real | No | 0.00 |
gargoyleBladeDummy | unit | No | |
gargoyleBladeFacing | real | No | |
gargoyleBladeGroup | group | No | |
gargoyleBladePoints | location | Yes | |
gargoyleClutchCaster | unit | No | |
gargoyleClutchEffect | effect | No | |
gargoyleClutchFree | boolean | No | |
gargoyleClutchPoints | location | Yes | |
gargoyleClutchTarget | unit | No | |
gargoyleClutchTimer | real | No | |
gargoyleItemBool | boolean | Yes | |
gargoyleStatueActiveUnit | unit | No | |
gargoyleUnit | unit | No | |
gruntBerserkerRageCounter | integer | No | |
gruntBerserkerRageUnit | unit | No | |
gruntBloodyBrawlerInt | integer | No | |
gruntBloodyBrawlerUnit | unit | No | |
gruntItemBool | boolean | Yes | |
gruntStaggeringSmashCaster | unit | No | |
gruntStaggeringSmashGroup | group | No | |
gruntStaggeringSmashTimer | timer | No | |
gruntUnit | unit | No | |
heroCanBecomeVulnerable | boolean | Yes | true |
heroCanRegenMana | boolean | Yes | true |
huntressFallingStarCaster | unit | No | |
huntressFallingStarEffect | effect | No | |
huntressFallingStarEffect2 | effect | No | |
huntressFallingStarHeight | real | No | |
huntressFallingStarPoint | location | No | |
huntressItemBool | boolean | Yes | |
huntressUnit | unit | No | |
hydraBreathPoints | location | Yes | |
hydraGeyserCount | integer | No | |
hydraGeyserEffect | effect | Yes | |
hydraGeyserInt | integer | Yes | |
hydraItemBool | boolean | Yes | |
hydraMawInt | integer | No | |
hydraMawUnit | unit | No | |
hydraSongBool | boolean | No | |
hydraUnit | unit | No | |
ID | integer | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsUnitPreplaced | boolean | Yes | |
knightConchDistance | real | No | |
knightConchDistanceTraveled | real | No | |
knightConchGroup | group | No | |
knightConchInt | integer | No | |
knightConchPoints | location | Yes | |
knightConchUnit | unit | No | |
knightConchVelocity | real | No | 25.00 |
knightIllusionUnit | unit | Yes | |
knightItemBool | boolean | Yes | |
knightUnit | unit | No | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
mb | multiboard | No | |
mbDeaths | integer | Yes | |
mbDeathsTotal | integer | Yes | |
mbEliminations | integer | Yes | |
mbElimTotal | integer | Yes | |
mbHeroIcon | string | Yes | |
mbXPEarned | real | Yes | 0.00 |
myVisVar | fogmodifier | No | |
NextDamageType | integer | No | |
objDelayReal | real | No | 60.00 |
objDuelInt | integer | No | |
objDuelUnit | unit | Yes | |
objEffect | effect | Yes | |
objInProgress | boolean | No | |
objMinimapIcon | minimapicon | Yes | |
objRampIncrement | real | No | 0.10 |
objRampReal | real | No | 1.00 |
objSounds | sound | Yes | |
objStartDelayReal | real | No | 420.00 |
objTimer | timer | Yes | |
objTimerWindow | timerdialog | Yes | |
objTotalComplete | integer | No | |
objVisibility | fogmodifier | Yes | |
overlordUnit | unit | No | |
playerForce | force | Yes | |
playerHero | unit | Yes | |
playerIsDead | boolean | Yes | |
playerNumber | integer | No | |
playerPoint | location | No | |
playerRespawnTimer | timer | Yes | |
playerRespawnTimerInt | integer | Yes | |
playerRespawnTimerWindow | timerdialog | Yes | |
playerShop | unit | Yes | |
playerShopElite | unit | Yes | |
playerSpawner | unit | Yes | |
priestItemBool | boolean | Yes | |
priestTimer | timer | No | |
priestUnit | unit | No | |
shamanBloodlustBool | boolean | No | |
shamanItemBool | boolean | Yes | |
shamanMagmaCount | integer | No | |
shamanMagmaDelay | integer | No | 3 |
shamanMagmaEffect | effect | Yes | |
shamanMagmaInt | integer | Yes | |
shamanMagmaPoint | location | Yes | |
shamanShieldDebuffEffect | effect | Yes | |
shamanShieldDebuffGroup | group | No | |
shamanShieldGroup | group | No | |
shamanShieldUnit | unit | Yes | |
shamanShieldUnitDuration | real | Yes | |
shamanShieldUnitStack | integer | Yes | |
shamanUnit | unit | No | |
spawnInterval | integer | No | 100 |
spawnPointAlliance | location | Yes | |
spawnPointHorde | location | Yes | |
spawnRateTimer | timer | No | |
spellBreakerAbsorbEffect | effect | No | |
spellBreakerAbsorbInt | integer | No | |
spellBreakerAbsorbTarget | unit | No | |
spellBreakerAuraEffect | effect | Yes | |
spellBreakerAuraInt | integer | No | |
spellBreakerAuraX | integer | Yes | |
spellBreakerAuraY | integer | Yes | |
spellBreakerItemBool | boolean | Yes | |
spellBreakerMaxRuneDur | real | No | 5.00 |
spellBreakerReflectBool | boolean | No | |
spellBreakerReflectHits | integer | Yes | |
spellBreakerReflectInt | integer | No | |
spellBreakerRuneCount | integer | Yes | |
spellBreakerRuneDuration | real | Yes | 0.00 |
spellBreakerRuneEffect | effect | Yes | |
spellBreakerRuneGroup | group | No | |
spellBreakerUnit | unit | No | |
taurenChargeCaster | unit | No | |
taurenChargeHashtable | hashtable | No | |
taurenChargeLeapers | group | No | |
taurenChargePoints | location | Yes | |
taurenChargeReal | real | Yes | |
taurenChargeTarget | unit | No | |
taurenItemBool | boolean | Yes | |
taurenPulverizeInt | integer | No | |
taurenPulverizeRequired | integer | No | 3 |
taurenTectonicEffect | effect | Yes | |
taurenTectonicGroup | group | No | |
taurenTectonicInt | integer | No | 0 |
taurenUnit | unit | No | |
tempdummy | unit | Yes | |
tempeffect | effect | Yes | |
tempgroup | group | No | |
tempgroup2 | group | No | |
tempint | integer | No | |
tempint2 | integer | No | |
tempplayer | player | No | |
temppoint | location | No | |
temppoint2 | location | No | |
temppoint3 | location | No | |
temppoint4 | location | No | |
temppoint5 | location | No | |
tempreal | real | No | |
tempreal2 | real | No | |
tempstring | string | No | |
tempstring2 | string | No | |
temptargetarray | unit | Yes | |
tempunit | unit | No | |
tempunit2 | unit | No | |
tempunit3 | unit | No | |
tempvisibility | fogmodifier | Yes | |
tuskarrHealerFishGroup | group | Yes | |
tuskarrHealerFishInt | integer | Yes | |
tuskarrHealerFishUnit | unit | Yes | |
tuskarrHealerGlaciateEffect | effect | No | |
tuskarrHealerGlaciateInt | integer | No | |
tuskarrHealerItemBool | boolean | Yes | |
tuskarrHealerRiptideInt | integer | No | |
tuskarrHealerRiptideUnit | unit | No | |
tuskarrHealerUnit | unit | No | |
UDex | integer | No | |
UDexUnits | unit | Yes | |
UnitIndexerEnabled | boolean | No | true |
UnitIndexEvent | real | No | |
victoryGameIsOver | boolean | No | |
victoryPlayer | player | No | |
victoryTimerInt | integer | No | |
waveInterval | integer | No | 0 |
WEAPON_TYPE_AM_CHOP | integer | No | |
WEAPON_TYPE_CH_SLICE | integer | No | |
WEAPON_TYPE_CL_SLICE | integer | No | |
WEAPON_TYPE_CM_SLICE | integer | No | |
WEAPON_TYPE_MH_BASH | integer | No | |
WEAPON_TYPE_MH_CHOP | integer | No | |
WEAPON_TYPE_MH_SLICE | integer | No | |
WEAPON_TYPE_MH_STAB | integer | No | |
WEAPON_TYPE_ML_CHOP | integer | No | |
WEAPON_TYPE_ML_SLICE | integer | No | |
WEAPON_TYPE_MM_BASH | integer | No | |
WEAPON_TYPE_MM_CHOP | integer | No | |
WEAPON_TYPE_MM_SLICE | integer | No | |
WEAPON_TYPE_MM_STAB | integer | No | |
WEAPON_TYPE_NONE | integer | No | |
WEAPON_TYPE_RH_BASH | integer | No | |
WEAPON_TYPE_WH_BASH | integer | No | |
WEAPON_TYPE_WH_SLICE | integer | No | |
WEAPON_TYPE_WL_BASH | integer | No | |
WEAPON_TYPE_WL_SLICE | integer | No | |
WEAPON_TYPE_WL_STAB | integer | No | |
WEAPON_TYPE_WM_BASH | integer | No | |
WEAPON_TYPE_WM_SLICE | integer | No | |
WEAPON_TYPE_WM_STAB | integer | No | |
WeaponTypeDebugStr | string | Yes | |
WispCollapseBenefitedGroup | group | No | |
WispCollapseCaster | unit | No | |
WispCollapseChargeReal | real | No | |
WispCollapseDamage | real | No | |
WispCollapseDistance | real | No | 800.00 |
WispCollapseEffect | effect | Yes | |
WispCollapseOrbCount | integer | No | 0 |
WispCollapseOrbPoint | location | Yes | |
WispCollapseSpeed | real | No | 6.00 |
WispCollapseTraveledInt | integer | No | |
wispItemBool | boolean | Yes | |
WispStarBombCaster | unit | No | |
WispStarBombEffect | effect | No | |
WispStarBombInt | integer | No | |
WispStarBombUnit | unit | No | |
WispStructureLimit | integer | No | 2 |
WispStructureUnitGroup | group | No | |
WispUnit | unit | No | |
zEliteShopRemove | boolean | No | true |
zFastModeEnable | boolean | No | |
zGlobalMovementOverride | integer | Yes | |
zGlobalUpgradeInterval | real | No | 240.00 |
zGlobalUpgradeLevel | integer | No | |
zGlobalUpgradeTimer | timer | No | |
zGlobalXPGroup | group | No | |
zGlobalXPUnit | unit | Yes | |
zGlobalXPVal | integer | No | 3 |
zPlayerHasLeft | boolean | Yes | |
ZplayerHeroDeadCount | integer | No | |
ZplayerHeroId | string | Yes | |
zPlayerInTutorial | boolean | Yes | |
ztempability | abilcode | No |
--Global Initialization 1.1 also hooks the InitCustomTriggers and RunInitializationTriggers functions
do
local iFuncs = {}
function onInitialization(func) -- Runs once all Map Initialization triggers are executed
iFuncs[func] = func
end
local function runInitialization()
for k, f in pairs(iFuncs) do f() end
iFuncs = nil
end
local tFuncs = {}
function onTriggerInit(func) -- Runs once all InitTrig_ functions are called
tFuncs[func] = func
end
local function runTriggerInit()
for k, f in pairs(tFuncs) do f() end
tFuncs = nil
local old = RunInitializationTriggers
if old then
function RunInitializationTriggers()
old()
runInitialization()
end
else
runInitialization()
end
end
local gFuncs = {}
function onGlobalInit(func) --Runs once all udg_ globals are set.
gFuncs[func] = func --Simplification thanks to TheReviewer and Zed on Hive Discord
end
local function runGlobalInit()
for k, f in pairs(gFuncs) do f() end
gFuncs = nil
local old = InitCustomTriggers
if old then
function InitCustomTriggers()
old()
runTriggerInit()
end
else
runTriggerInit()
end
end
local oldBliz = InitBlizzard
function InitBlizzard()
oldBliz()
local old = InitGlobals
if old then
function InitGlobals()
old()
runGlobalInit()
end
else
runGlobalInit()
end
end
end
-- - - - - - - - - - - -
local mui = {}
local mui_data = {}
-- - - - - - - - - - - -
mui_data.frameStack = {} -- where custom frames are kept for reference.
mui_data.aspectRatio = {} -- each player's aspect ratio.
-- - - - - - - - - - - -
mui_data.portraitSize = 0.0225 -- height and width.
mui_data.maxPlayers = 9 -- (0-based) how many players can play the map (starts at Player(0))
-- - - - - - - - - - - -
mui_data.consoleUIOffsetY = -0.24 -- pushes the console UI originframe down.
mui_data.minimapBtnPadding = 0.02365 -- spaces out the minimap buttons.
mui_data.menuBtnOffsetY = 0.0218 -- distance from bottom row of command bar.
mui_data.menuBtnPadding = 0.0015 -- menu button padding bottom and right.
mui_data.menuBtnWidth = 0.065 -- resized width.
mui_data.infoPanelOffsetX = 0.4 -- offset from left of screen.
mui_data.infoPanelOffsetY = 0.00325 -- offset from bottom of screen.
mui_data.infoPanelGutterY = 0.004 -- nudges the backdrop up for edge alignment.
mui_data.resourceBarOffsetX = -0.004 -- moves backdrop left and right
mui_data.resourceBarOffsetY = -0.0005 -- moves backdrop up and down.
mui_data.inventoryPadding = 0.0256 -- space between inventory buttons
mui_data.inventoryOffsetX = -0.075 -- pushes inventory buttons left of info panel.
mui_data.inventoryOffsetY = 0.0041 -- starting Y offset.
-- - - - - - - - - - - -
mui_data.commandBarFrameSize = 0.029 -- somewhat arbitrary frame width that is hardcoded and scary to change. calcs length.
mui_data.commandBarGutterY = 0.007 -- gap between top and bottom rows.
mui_data.commandBarOffsetX = 0.315 -- start X is calced based on icon size. this value nudges the command bar left or right.
mui_data.commandBarOffsetY = 0.065 -- offset from bottom of screen.
mui_data.commandBarPadding = 0.01 -- gap between command bar buttons.
mui_data.commandBarNudgeDiv = 2.26 -- psuedo-combined width, gutter, offset ratio. lower = move command bar left, vice versa.
mui_data.commandBarSize = 0.035 -- height and width.
mui_data.commandBarOrder = {0,1,2,3,4,5,8,9,10,11,6,7} -- requires 12 items. arranges the command bar top-to-bottom. see below for default rubric.
-- - - - - - - - - - - -
mui_data.getCenter = 0.4 -- approximate center of the original UI.
mui_data.getMaxY = 0.6 -- top edge of 4:3 screen.
mui_data.inventoryTxtAlpha = 225 -- 'Inventory' over the inv. pane.
mui_data.unitInfoHeight = 0.605 -- controls the state bar and hero portrait Y offset from center (percentage of parent info frame).
mui_data.unitInfoWidth = 0.4265 -- controls the state bar and hero portrait X offset from center (percentage of parent info frame).
mui_data.unitStateTxtSize = 0.005 -- reduces the size of health and mana text so it doesn't overlap portrait.
mui_data.backdropAlpha = 130 -- (0 to 255-based int) set alpha for floating UI elements (default = ~51%).
mui_data.selectedCount = {} -- recycled variable for timer logic.
mui_data.focusUnit = {} -- recycled variable for timer logic.
mui_data.screenGutter = mui_data.infoPanelGutterY + mui_data.infoPanelOffsetY
mui_data.txtColor = '|cffffffff'
mui_data.textureMain = 'war3mapImported\\ui_black_colorizer.tga'
mui_data.textureHighlight = 'war3mapImported\\ui_white_colorizer.blp'
mui_data.textureOverlay = 'war3mapImported\\ui_halftone_grey_colorizer.blp'
mui_data.textureFlagBTN = 'ReplaceableTextures\\CommandButtons\\BTNRallyPoint.blp'
-- - - - - - - - - - - -
--[[
original command bar matrix (reference):
row1: | 0| | 1| | 2| | 3|
row2: | 4| | 5| | 6| | 7|
row3: | 8| | 9| |10| |11|
default mui command bar matrix (reference):
top: | 0| | 1| | 2| | 3| | 4| | 5|
bot: | 8| | 9| |10| |11| | 6| | 7|
--]]
-- - - - - - - - - - - -
-- iterate through command buttons:
mui_data.commandBarMap = {
[0] = "CommandButton_0",
[1] = "CommandButton_1",
[2] = "CommandButton_2",
[3] = "CommandButton_3",
[4] = "CommandButton_4",
[5] = "CommandButton_5",
[6] = "CommandButton_6",
[7] = "CommandButton_7",
[8] = "CommandButton_8",
[9] = "CommandButton_9",
[10] = "CommandButton_10",
[11] = "CommandButton_11",
}
-- unhide desired components:
mui_data.unhideFramesByName = {
[0] = "MiniMapFrame",
[1] = "Multiboard",
[2] = "UpperButtonBarFrame",
[3] = "SimpleUnitStatsPanel",
[4] = "SimpleHeroLevelBar",
[5] = "ResourceBarFrame",
[6] = "MinimapButtonBar"
}
mui_data.minimapButtons = {
[0] = "FormationButton",
[1] = "MiniMapCreepButton",
[2] = "MiniMapAllyButton",
[3] = "MiniMapTerrainButton",
[4] = "MinimapSignalButton"
}
mui_data.inventoryButtons = {
[0] = "InventoryButton_0",
[1] = "InventoryButton_1",
[2] = "InventoryButton_2",
[3] = "InventoryButton_3",
[4] = "InventoryButton_4",
[5] = "InventoryButton_5"
}
mui_data.menuButtons = {
[0] = "UpperButtonBarQuestsButton",
[1] = "UpperButtonBarMenuButton",
[2] = "UpperButtonBarAlliesButton",
[3] = "UpperButtonBarChatButton"
}
-- rearrange originframes that were moved off screen:
mui_data.consoleFrames = {
[0] = { frame = ORIGIN_FRAME_UNIT_MSG, x = 0.0, y = 0.25},
}
function mui.Init()
for pInt = 0,mui_data.maxPlayers do
if Player(pInt) == GetLocalPlayer() then
-- temporary framehandle variable for multiple lookups + readability:
local fh
-- will stop the moving of frames when changing resolution or window mode when set to false:
BlzEnableUIAutoPosition(false)
-- move undesired items off screen:
BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_BOTTOM, 0.0, mui_data.consoleUIOffsetY)
-- fetch items we use repeatedly:
mui_data.gameUI = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI,0)
mui_data.portrait = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT,0)
mui_data.chatMsg = BlzGetOriginFrame(ORIGIN_FRAME_CHAT_MSG,0)
mui_data.infoPanel = BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail",0))
mui_data.minimap = BlzGetFrameByName("MiniMapFrame",0)
mui_data.minimapBar = BlzGetFrameByName("MinimapButtonBar",0)
mui_data.resourceBar = BlzGetFrameByName("ResourceBarFrame",0)
mui_data.inventoryTxt = BlzGetFrameByName("InventoryText", 0)
-- reposition items move after nudging ConsoleUI off screen:
for i = 0,#mui_data.consoleFrames do
fh = BlzGetOriginFrame(mui_data.consoleFrames[i].frame,0)
BlzFrameClearAllPoints(fh)
BlzFrameSetAbsPoint(fh,FRAMEPOINT_BOTTOMLEFT,mui_data.consoleFrames[i].x,mui_data.consoleFrames[i].y + mui_data.screenGutter)
end
fh = BlzGetFrameByName("MiniMapFrame",0)
BlzFrameClearAllPoints(fh)
BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.0, 0.0 + mui_data.screenGutter)
BlzFrameClearAllPoints(mui_data.chatMsg)
-- for some reason the chat frame ignores x offset:
BlzFrameSetPoint(mui_data.chatMsg, FRAMEPOINT_BOTTOMLEFT, mui_data.minimap, FRAMEPOINT_TOPLEFT, 0.0, 0.0)
BlzFrameSetSize(mui_data.chatMsg, 0.5, 0.2)
-- unhide:
for i = 0,#mui_data.unhideFramesByName do
BlzFrameSetVisible(BlzGetFrameByName(mui_data.unhideFramesByName[i],0), true)
end
for i = 0,#mui_data.inventoryButtons do
BlzFrameSetVisible(BlzGetFrameByName(mui_data.inventoryButtons[i],0), true)
end
for i = 0,#mui_data.menuButtons do
BlzFrameSetVisible(BlzGetFrameByName(mui_data.menuButtons[i],0), true)
end
-- hide:
BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
BlzFrameSetVisible(BlzFrameGetParent(BlzGetFrameByName("ResourceBarFrame",0)), false)
-- unhide specific frames:
for i = 0,4 do
BlzFrameSetVisible(BlzGetOriginFrame(ORIGIN_FRAME_MINIMAP_BUTTON,i), true)
end
BlzFrameSetVisible(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BAR,0), true)
BlzFrameSetVisible(mui_data.infoPanel,true)
-- cover cannot be hidden, has to be made 100% transparent:
fh = BlzGetFrameByName("SimpleInventoryCover", 0)
BlzFrameSetAlpha(fh, 0)
BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 1.0,1.0)
-- set custom transparencies:
BlzFrameSetAlpha(mui_data.inventoryTxt, mui_data.inventoryTxtAlpha)
-- set custom text overrides:
BlzFrameSetText(mui_data.inventoryTxt, mui_data.txtColor .. 'Inventory|r')
-- move specific frames (these need to be done before backdrops are generated):
mui.UnitPortraitAttachContainer() -- move unit portrait.
mui.UnitStatsPanelMove() -- reposition unit stats panel.
mui.CommandBarMove() -- load command bar.
mui.MinimapMove() -- reposition minimap buttons.
mui.InventoryMove() -- reposition inventory icons.
mui.MenuButtonMove() -- moves top left menu buttons.
-- create custom frames, assign stored index for future manipulation:
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.minimap,'minimap_bd',mui_data.textureMain)
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.infoPanel,
'unitinfo_bd',mui_data.textureMain,mui_data.backdropAlpha,nil,mui_data.infoPanelGutterY)
for i = 0,5 do
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(BlzGetFrameByName("InventoryButton_"..i,0),
'inv_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
end
for i = 0,4 do
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(BlzGetFrameByName(mui_data.minimapButtons[i],0),
'minimapbar_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
end
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.resourceBar,'resourcebar_bd',
mui_data.textureMain,mui_data.backdropAlpha, mui_data.resourceBarOffsetX, mui_data.resourceBarOffsetY)
end
end
-- keep outside of local player to stop desyncs:
-- mui.InitUnitSelectedUpdate()
end
function mui.UnitPortraitAttachContainer()
mui_data.portraitContainer = mui.AttachBackdropByHandle(mui_data.infoPanel, 'portrait_container',
mui_data.textureFlagBTN, 0, 0.0, BlzFrameGetHeight(mui_data.infoPanel)*mui_data.unitInfoHeight + 0.00672,
mui_data.portraitSize, mui_data.portraitSize)
BlzFrameSetAlpha(mui_data.portraitContainer, 0)
mui.UnitPortraitMove()
end
function mui.UnitPortraitMove()
BlzFrameClearAllPoints(mui_data.portrait)
BlzFrameSetSize(mui_data.portrait,mui_data.portraitSize,mui_data.portraitSize)
BlzFrameSetAbsPoint(mui_data.portrait,FRAMEPOINT_CENTER,mui_data.getCenter,BlzFrameGetHeight(mui_data.infoPanel) + 0.02265)
end
-- we have to do some janky stuff here to allow hp/text bars to update.
-- the short version of it is:
-- a) the unit selected needs to be present for all players or it can desync. we make one for each player so they are guaranteed to have visibility of it.
-- b) there is a few frames of delay between hp/mana text becoming enabled. trying to move it too early can cause a client crash.
function mui.InitUnitSelectedUpdate()
for pSlot = 0,9 do
local player = Player(pSlot)
TimerStart(NewTimer(), 0.1, false, function()
local u = CreateUnit(player, FourCC('hfoo'),0.0,0.0,270.0)
PauseUnit(u,true)
SetUnitVertexColorBJ(u, 0, 0, 0, 100)
if GetLocalPlayer() == player then
SelectUnit(u, true)
end
TimerStart(NewTimer(), 0.3, false, function()
if GetLocalPlayer() == player then
local fh = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_HP_TEXT, 0)
local fh2 = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_MANA_TEXT, 0)
BlzFrameClearAllPoints(fh)
BlzFrameSetPoint(fh, FRAMEPOINT_CENTER, mui_data.portraitContainer,
FRAMEPOINT_CENTER,-(mui_data.portraitSize*2.5),-0.0056)
BlzFrameClearAllPoints(fh2)
BlzFrameSetPoint(fh2, FRAMEPOINT_CENTER, mui_data.portraitContainer,
FRAMEPOINT_CENTER,mui_data.portraitSize*2.5,-0.0056)
SelectUnit(u, false)
end
RemoveUnit(u)
ReleaseTimer()
end)
ReleaseTimer()
end)
end
end
function mui.CommandBarMove()
local fh
local prevfh
-- where top 1st btn starts on screen (expanding right):
local top_row_x = mui_data.getCenter - (12*mui_data.commandBarFrameSize)/mui_data.commandBarNudgeDiv + mui_data.commandBarOffsetX
-- where bot 1st btn starts on screen (expanding right):
local bot_row_x = mui_data.getCenter - (12*mui_data.commandBarFrameSize)/mui_data.commandBarNudgeDiv + mui_data.commandBarOffsetX
local reorder_index
for i = 0,11 do
reorder_index = mui_data.commandBarOrder[i+1]
fh = BlzGetFrameByName(mui_data.commandBarMap[reorder_index],0)
BlzFrameClearAllPoints(fh)
if (i >= 6) then -- bot row
if (i ~= 6) then
prevfh = BlzGetFrameByName(mui_data.commandBarMap[mui_data.commandBarOrder[i]],0)
BlzFrameSetPoint(fh, FRAMEPOINT_LEFT, prevfh, FRAMEPOINT_RIGHT, mui_data.commandBarPadding, 0.0)
else
BlzFrameSetAbsPoint(fh,FRAMEPOINT_RIGHT,bot_row_x,mui_data.commandBarOffsetY - mui_data.commandBarGutterY)
end
else -- top row
if (i ~= 0) then
prevfh = BlzGetFrameByName(mui_data.commandBarMap[mui_data.commandBarOrder[i]],0)
BlzFrameSetPoint(fh, FRAMEPOINT_LEFT, prevfh, FRAMEPOINT_RIGHT, mui_data.commandBarPadding, 0.0)
else
BlzFrameSetAbsPoint(fh,FRAMEPOINT_RIGHT,top_row_x,
mui_data.commandBarOffsetY+mui_data.commandBarFrameSize+mui_data.commandBarPadding)
end
end
BlzFrameSetSize(fh,mui_data.commandBarSize,mui_data.commandBarSize)
BlzFrameSetLevel(fh, 1)
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(fh,'cmd_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
end
end
function mui.InventoryMove()
local fh
local nudgeX = 0.0
local nudgeY = 0.0
-- inventory txt:
BlzFrameClearAllPoints(mui_data.inventoryTxt)
BlzFrameSetPoint(mui_data.inventoryTxt,FRAMEPOINT_TOPRIGHT,mui_data.infoPanel,FRAMEPOINT_TOPLEFT,
mui_data.inventoryOffsetX + BlzFrameGetWidth(mui_data.inventoryTxt)/2 + mui_data.inventoryPadding/4.33,
mui_data.inventoryOffsetY + mui_data.inventoryPadding*0.65)
-- inventory buttons:
for i = 0,5 do
fh = BlzGetFrameByName(mui_data.inventoryButtons[i],0)
if i == 2 or i == 4 then
nudgeY = nudgeY + -(BlzFrameGetWidth(fh)/2 + mui_data.inventoryPadding)
end
if i == 1 or i == 3 or i == 5 then
nudgeX = BlzFrameGetWidth(fh)/2 + mui_data.inventoryPadding
else
nudgeX = 0.0
end
BlzFrameClearAllPoints(fh)
BlzFrameSetPoint(fh,FRAMEPOINT_TOPRIGHT,mui_data.infoPanel,FRAMEPOINT_TOPLEFT,
mui_data.inventoryOffsetX + nudgeX, mui_data.inventoryOffsetY + nudgeY)
end
end
function mui.MenuButtonMove()
local fh
for i = 0,#mui_data.menuButtons do
fh = BlzGetFrameByName(mui_data.menuButtons[i],0)
BlzFrameClearAllPoints(fh)
BlzFrameSetSize(fh, mui_data.menuBtnWidth, BlzFrameGetHeight(fh))
--BlzFrameSetTexture(fh, mui_data.textureMain, 0, true)
--[[BlzFrameSetAlpha(fh,0.0)
mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(fh,'menu_bd_'..i,
mui_data.textureMain,mui_data.backdropAlpha,0.0,0.0)--]]
end
BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[0],0),FRAMEPOINT_TOPLEFT,BlzGetFrameByName(mui_data.commandBarMap[8],0),
FRAMEPOINT_BOTTOMLEFT,-mui_data.menuBtnPadding,-(mui_data.menuBtnOffsetY - mui_data.commandBarGutterY))
BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[1],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[0],0),
FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[2],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[1],0),
FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[3],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[2],0),
FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
end
function mui.UnitStatsPanelMove()
BlzFrameClearAllPoints(mui_data.infoPanel)
BlzFrameSetAbsPoint(mui_data.infoPanel,FRAMEPOINT_BOTTOM,mui_data.infoPanelOffsetX,mui_data.infoPanelOffsetY)
end
function mui.MinimapMove()
local fh
for i = 0,4 do
fh = BlzGetFrameByName(mui_data.minimapButtons[i],0)
BlzFrameClearAllPoints(fh)
BlzFrameSetPoint(fh, FRAMEPOINT_BOTTOMLEFT, mui_data.minimap, FRAMEPOINT_BOTTOMRIGHT,0.005,mui_data.minimapBtnPadding*i)
end
end
-- must be run after a cinematic or hero portrait resets position:
function mui.AfterCinematic()
BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
BlzFrameSetVisible(BlzGetFrameByName("CinematicPortrait",0), false)
BlzFrameSetAlpha(BlzGetFrameByName("SimpleInventoryCover", 0), 0)
BlzFrameSetAbsPoint(BlzGetFrameByName("SimpleInventoryCover", 0), FRAMEPOINT_BOTTOMLEFT, 1.0,1.0)
BlzFrameSetAbsPoint(mui_data.chatMsg,FRAMEPOINT_BOTTOMLEFT,0.15,0.20)
mui.UnitPortraitMove()
end
-- create a backdrop and set it to be positioned at the desired frame via FRAMEPOINT_CENTER.
-- @fh = target this frame
-- @newFrameNameString = enter a custom value to manipulate backdrop by name if needed e.g. "myInventoryBackdrop"
-- @texturePathString = [optional; usually needed] texture (.blp) as the background
-- @alphaValue = [optional] if the backdrop should be transparent, pass in a value 0-255
-- @offsetx = [optional] nudge the frame left or right (percentage of screen)
-- @offsety = [optional] nudge the frame up or down (percentage of screen)
-- @width, @height = [optional] override the frame size
-- :: returns framehandle
function mui.AttachBackdropByHandle(fh, newFrameNameString, texturePathString, alphaValue, offsetx, offsety, width, height)
local nh = BlzCreateFrameByType("BACKDROP", newFrameNameString, BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
local x = offsetx or 0.0
local y = offsety or 0.0
local w = width or BlzFrameGetWidth(fh)
local h = height or BlzFrameGetHeight(fh)
BlzFrameSetSize(nh, w, h)
BlzFrameSetPoint(nh, FRAMEPOINT_CENTER, fh, FRAMEPOINT_CENTER, 0.0 + x, 0.0 + y)
if texturePathString then
BlzFrameSetTexture(nh, texturePathString, 0, true)
end
BlzFrameSetLevel(nh, 0)
if alphaValue then
BlzFrameSetAlpha(nh, math.ceil(alphaValue))
end
return nh
end
-- show or hide custom made UI elements during a cinematic (be sure to use local player only)
-- @bool = should it be visible? true = visible; false = hide
function mui.ShowHideCustomUI(bool)
for i = 1,#mui_data.frameStack do
if mui_data.frameStack[i] ~= nil then
BlzFrameSetVisible(mui_data.frameStack[i], bool)
end
end
end
do
-- remove fog of war effects from cinematic mode function:
function CinematicModeExBJ(cineMode, forForce, interfaceFadeTime)
if (not bj_gameStarted) then
interfaceFadeTime = 0
end
if (cineMode) then
if (not bj_cineModeAlreadyIn) then
bj_cineModeAlreadyIn = true
end
if (IsPlayerInForce(GetLocalPlayer(), forForce)) then
ClearTextMessages()
ShowInterface(false, interfaceFadeTime)
EnableUserControl(false)
EnableOcclusion(false)
SetCineModeVolumeGroupsBJ()
mui.ShowHideCustomUI(false)
end
SetMapFlag(MAP_LOCK_SPEED, true)
else
bj_cineModeAlreadyIn = false
if (IsPlayerInForce(GetLocalPlayer(), forForce)) then
ShowInterface(true, interfaceFadeTime)
EnableUserControl(true)
EnableOcclusion(true)
VolumeGroupReset()
EndThematicMusic()
CameraResetSmoothingFactorBJ()
mui.ShowHideCustomUI(true)
mui.AfterCinematic()
end
SetMapFlag(MAP_LOCK_SPEED, false)
end
end
end
-- system data (do not change)
local mvp = {}
local mvp_data = {}
mvp_data.playerName = {} -- optional var; store original player names to prevent color code overload.
mvp_data.x = GetRectCenterX(gg_rct_mapCenter)
mvp_data.y = GetRectCenterY(gg_rct_mapCenter)
mvp_data.pVictory = {} -- flag: if the player won the game.
mvp_data.pDmg = {} -- score: track damage.
mvp_data.pHealing = {} -- score: track healing.
mvp_data.pAbsorbed = {} -- score: track damage absorbed.
mvp_data.pElim = {} -- score: track eliminations.
mvp_data.pDeath = {} -- score: track deaths.
mvp_data.pExp = {} -- score: track experience earned.
mvp_data.pObjCap = {} -- score: track objective participation.
mvp_data.pClutch = {} -- score: track clutch events (e.g. a life-saving heal).
mvp_data.weight = {} -- initiate score weights.
mvp_data.pScore = {} -- a player's score.
-- score config:
mvp_data.weight.damage = 0.50 -- how much value a damage point is worth.
mvp_data.weight.absorb = 1.15 -- how much value a damage point is worth.
mvp_data.weight.healing = 0.90 -- how much value a healing point is worth.
mvp_data.weight.experience = 5.00 -- how much value earned experience is worth.
mvp_data.weight.deathFactor = 0.98 -- multiply the final score for a penalty per death.
mvp_data.weight.elimFactor = 1.02 -- multiply the final score for a bonus per elimination.
mvp_data.weight.clutchFactor = 1.01 -- multiply the final score for a bonus per clutch event (e.g. healing a very low health ally).
mvp_data.weight.objFactor = 1.10 -- multiply the final score for a bonus per completed event (e.g. near an objective capture).
-- system config:
mvp_data.maxPlayers = 10 -- how many players exist.
mvp_data.sepDist = 120.0 -- how far aparts heroes are.
mvp_data.camDist = 1233.0 -- how far the camera angle is.
mvp_data.camRot = 315.0 -- how the camera is angled.
mvp_data.teamDist = 575.0 -- starting distance of each team row from x,y center.
mvp_data.mvpNudge = 125.0 -- how far the MVP hero is pushed forward.
mvp_data.queuedDelay = 1.5 -- the delay before the MVP system initiates.
mvp_data.enterDelay = 0.25 -- how fast each hero enters the MVP formation.
mvp_data.selectDelay = 2.55 -- the delay before an MVP is chosen.
mvp_data.lbDelay = 2.55 -- the delay before the scoreboard shows.
mvp_data.teamColor = true -- enable to set heroes to matching colors for team clarity.
mvp_data.mvpColor = true -- reverts the selected MVP back to their original color for individual player identification.
mvp_data.teamIntSplit = 6 -- the first player number (1-based) of team B (e.g. in a 5v5, typically is player 6).
mvp_data.backdropAlpha = 190 -- transparency of leaderboard background.
mvp_data.teamColorA = PLAYER_COLOR_MAROON -- team A color if teamColor is enabled.
mvp_data.teamColorB = PLAYER_COLOR_NAVY -- team B color if teamColor is enabled.
mvp_data.mvpAnim = 'stand victory' -- played animation for selected MVP.
mvp_data.effectMVP1 = 'Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl'
mvp_data.effectMVP2 = 'Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl'
mvp_data.enterEffect = 'Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdl'
mvp_data.textureMain = 'war3mapImported\\ui_black_colorizer.tga'
mvp_data.teamTxtColorA = '|cffff2c2c'
mvp_data.teamTxtColorB = '|cff007dff'
mvp_data.titleColor = '|cff00ffff'
mvp_data.mvpTxtC = '|cffffff00'
mvp_data.showHideBtn = {}
mvp_data.titles = { -- column headers
[1] = 'Hero',
[2] = 'Player',
[3] = 'EXP Gained',
[4] = 'Obj. Caps',
[5] = 'Kills',
[6] = 'Deaths',
[7] = 'Damage',
[8] = 'Healing',
[9] = 'Absorbs',
[10] = 'Clutch Spells',
[11] = 'Score'
}
mvp_data.textColors = { -- color of the text in each column
[1] = '|cffffffff',
[2] = '|cffffffff',
[3] = '|cffff00ff',
[4] = '|cffffffff',
[5] = '|cffffffff',
[6] = '|cffffffff',
[7] = '|cffff8505',
[8] = '|cff5aff5a',
[9] = '|cff00b1ff',
[10] = '|cff8080ff',
[11] = '|cffffaf00'
}
-- col:
-- :: hero icon | player name | exp earned | objcap | elims | deaths | dmg | healing | absorbs | clutch events | final score
-- rows:
-- :: Hero | Player | EXP | Obj Completed | Eliminations | Deaths | Damage Done | Healing Done | Damage Absorbed | Clutch Spells | Score
-- :: [player 1]
-- :: . . .
-- :: [player 10]
-- set default values
function mvp.Init()
Preload(mvp_data.enterEffect)
Preload(mvp_data.effectMVP1)
Preload(mvp_data.effectMVP2)
for pInt = 1,mvp_data.maxPlayers do -- 1-based to match GUI's player index
mvp_data.pDmg[pInt] = 0
mvp_data.pHealing[pInt] = 0
mvp_data.pAbsorbed[pInt] = 0
mvp_data.pClutch[pInt] = 0
mvp_data.pObjCap[pInt] = 0
-- if undesired, delete this and handle populated leaderboard names yourself:
mvp_data.playerName[pInt] = GetPlayerName(Player(pInt-1))
end
end
-- queue mvp screen
function mvp.Initiate()
-- remove units in center of map:
mvp.ClearArea()
-- setup camera and heroes:
for pInt = 1,mvp_data.maxPlayers do -- 1-based
mvp.ShowHero(pInt, false)
mvp.SetCameraTimer(pInt)
-- revive heroes if dead then move them
if udg_playerHero[pInt] then
-- if dead, revive:
if not IsUnitAliveBJ(udg_playerHero[pInt]) then
ReviveHero(udg_playerHero[pInt],-1000,800,false)
end
-- re-initiate freeze just in case
PauseUnit(udg_playerHero[pInt],true)
SetUnitInvulnerable(udg_playerHero[pInt],true)
if mvp_data.teamColor and pInt < mvp_data.teamIntSplit then
SetUnitColor( udg_playerHero[pInt], mvp_data.teamColorA )
elseif mvp_data.teamColor then
SetUnitColor( udg_playerHero[pInt], mvp_data.teamColorB )
end
-- get scores:
mvp_data.pScore[pInt] = mvp.CalculateScore(pInt)
end
end
-- begin timer to place heroes:
local xOffset, yOffset, distance, offset, angle, pInt = 0.0, 0.0, mvp_data.teamDist, -mvp_data.sepDist, 180.0, 1
TimerStart(NewTimer(),mvp_data.queuedDelay,false,function() -- do a short wait
TimerStart(NewTimer(),mvp_data.enterDelay,true,function()
-- flip projection angle for team B, moving them right of x,y instead of left.
if pInt == mvp_data.teamIntSplit then
angle = 360.0
distance = mvp_data.teamDist
end
xOffset, yOffset = PolarProjectionXY(mvp_data.x, mvp_data.y, distance, angle)
if udg_playerHero[pInt] then
mvp.ShowHero(pInt, true)
UnitRemoveBuffs(udg_playerHero[pInt], true, true)
SetUnitX(udg_playerHero[pInt],xOffset)
SetUnitY(udg_playerHero[pInt],yOffset)
SetUnitFacing(udg_playerHero[pInt],270.0)
SetUnitPathing(udg_playerHero[pInt],false)
SetUnitLifePercentBJ(udg_playerHero[pInt], 100)
DestroyEffect(AddSpecialEffect(mvp_data.enterEffect,xOffset,yOffset))
end
distance = distance + offset
pInt = pInt + 1
if pInt > mvp_data.maxPlayers then
-- delay before an MVP is chosen:
TimerStart(NewTimer(),mvp_data.selectDelay,false,function()
mvp_data.mvpWinner = mvp.SelectMVP()
TimerStart(NewTimer(),mvp_data.lbDelay,false,function()
mvp.ScoreboardGenerate()
ReleaseTimer()
end)
ReleaseTimer()
end)
ReleaseTimer()
end
end)
end)
end
-- run an algorithm to calculate player's score; returns score
function mvp.CalculateScore(pInt) -- pass in 1-based value
-- for a copy paste solution, enable to map existing score variables:
-- mvp.MapVariableValues()
local score = 0
local h = mvp_data.pHealing[pInt] * mvp_data.weight.healing
local d = mvp_data.pDmg[pInt] * mvp_data.weight.damage
local a = mvp_data.pAbsorbed[pInt] * mvp_data.weight.absorb
local e = mvp_data.pExp[pInt] * mvp_data.weight.experience
score = h + d + a + e
-- bonuses:
if mvp_data.pElim[pInt] > 0 then
score = score + (score * (mvp_data.weight.elimFactor-1) * mvp_data.pElim[pInt])
end
if mvp_data.pClutch[pInt] > 0 then
score = score + (score * (mvp_data.weight.clutchFactor-1) * mvp_data.pClutch[pInt])
end
if mvp_data.pObjCap[pInt] > 0 then
score = score + (score * (mvp_data.weight.objFactor-1) * mvp_data.pObjCap[pInt])
end
-- penalties:
if mvp_data.pDeath[pInt] > 0 then
score = score - (score * ((1-mvp_data.weight.deathFactor) * mvp_data.pDeath[pInt]))
end
score = math.floor(score)
return score
end
-- get the highest score of the winning team.
function mvp.SelectMVP()
local pIntWinner = 1
local pIntHighScore = 0 -- default a score comparison.
for pInt = 1,10 do -- loop through every player and compare scores, shuffling winner value based on highest score.
if mvp_data.pScore[pInt] > pIntHighScore then -- if highest value is beat, set the temporary winner of the sort loop.
pIntWinner = pInt
pIntHighScore = mvp_data.pScore[pInt] -- set the new highest value to beat.
end
end
-- run mvp effects:
local x,y = PolarProjectionXY(GetUnitX(udg_playerHero[pIntWinner]),GetUnitY(udg_playerHero[pIntWinner]),mvp_data.mvpNudge,270.0)
if mvp_data.mvpColor then
SetUnitColor( udg_playerHero[pIntWinner], GetPlayerColor( Player(pIntWinner-1) ) )
end
SetUnitX(udg_playerHero[pIntWinner], x)
SetUnitY(udg_playerHero[pIntWinner], y)
DestroyEffect(AddSpecialEffect(mvp_data.effectMVP1, x, y))
DestroyEffect(AddSpecialEffect(mvp_data.effectMVP2, x, y))
SetUnitAnimation(udg_playerHero[pIntWinner], mvp_data.mvpAnim)
ClearTextMessages()
DisplayTextToForce( GetPlayersAll(), "|cff00b1ffMVP:|r " .. GetPlayerName( Player(pIntWinner-1) ) .. '|cff00b1ff!|r')
return pIntWinner
end
-- set up the camera
function mvp.SetCamera(pInt) -- pass in 1-based value
PanCameraToTimedForPlayer( Player(pInt-1), mvp_data.x, mvp_data.y, 0.0 )
SetCameraFieldForPlayer( Player(pInt-1), CAMERA_FIELD_TARGET_DISTANCE, mvp_data.camDist, 0.33 )
SetCameraFieldForPlayer( Player(pInt-1), CAMERA_FIELD_ANGLE_OF_ATTACK, mvp_data.camRot, 0.33 )
end
-- run a timer to keep camera fixed in place
function mvp.SetCameraTimer(pInt)
TimerStart(NewTimer(),0.03,true,function()
if GetLocalPlayer() == Player(pInt-1) then
mvp.SetCamera(pInt)
end
end)
end
-- show the UI component
-- @bool = true for show, false for hide
function mvp.ScoreboardShow(bool)
BlzFrameSetVisible(mvp_data.leaderboard_bd, bool)
end
-- create the UI component
-- this should only be called after players have chosen their heroes
function mvp.ScoreboardGenerate()
local lb_x = 0.84
local lb_y = 0.36
local lb_xpad = 0.03
local lb_ypad = 0.01
local lb_yoffset = 0.058
local count_rows = 11
local count_cols = 11
local cell_width = lb_x/count_rows*0.96
local cell_height = lb_y/count_cols
local cellPadding = cell_width*0.04
local lb_xnudge = cell_width*0.24 -- move the lb contents to the left slightly
local yOffset = 0.0
local xOffset = 0.0
local isIcon = false
local concatLength = 17 -- limit player name length to prevent overlap/ugliness
local fh -- temp handle for readability
local teamC -- team color text
mvp_data.lb_frames = {}
FloorPlayerScoreValues(pInt) -- flatten values
for pInt = 1,10 do
mvp_data.lb_frames[pInt] = {}
mvp_data.lb_frames[pInt].lb_table = {}
if udg_playerHero[pInt] then -- player present
if GetLocalPlayer() == Player(pInt-1) then
mvp_data.leaderboard_bd = mvp.AttachBackdropByHandle(mvp_data.gameUI, 'leaderboard_bd',
mvp_data.textureMain, mvp_data.backdropAlpha, 0.0, 0.0, lb_x, lb_y)
BlzFrameClearAllPoints(mvp_data.leaderboard_bd)
for col = 1,count_cols do -- columns
mvp_data.lb_frames[pInt].lb_table[col] = {}
xOffset = mvp.ScoreboardOffsetCalcX(col, cell_width, lb_xnudge + cellPadding)
for row = 1,count_rows do -- rows
-- player name color text:
if row < mvp_data.teamIntSplit+1 then -- team A
teamC = mvp_data.teamTxtColorA
else -- team B
teamC = mvp_data.teamTxtColorB
end
mvp_data.lb_frames[pInt].lb_table[col][row] = {}
yOffset = mvp.ScoreboardOffsetCalcY(row, cell_height)
if row > 1 and math.fmod(row,2) == 0 then
mvp_data.lb_frames[pInt].lb_table[col][row].bd = mvp.AttachBackdropByHandle(mvp_data.leaderboard_bd, 'leaderboard_bd_row-'..row,
mvp_data.textureMain, 33, 0.0, 0.0, xOffset, yOffset)
end
if row == 1 then -- create headers
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.titles[col])
elseif col == 1 then -- hero icon
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "BACKDROP")
BlzFrameSetTexture(fh, udg_mbHeroIcon[row-1], 0, true) -- sub 1 for header row
isIcon = true -- flag to size differently
elseif col == 2 then -- player name
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
if mvp_data.mvpWinner == row-1 then -- mvp text color
BlzFrameSetText(fh, mvp_data.mvpTxtC .. 'MVP:|r ' .. teamC .. string.sub(mvp_data.playerName[row-1],1,concatLength-5) .. '|r')
else
BlzFrameSetText(fh, teamC .. string.sub(mvp_data.playerName[row-1],1,concatLength) .. '|r') -- sub 1 for header row
end
elseif col == 3 then -- exp
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pExp[row-1])
elseif col == 4 then -- obj
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pObjCap[row-1])
elseif col == 5 then -- elims
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pElim[row-1])
elseif col == 6 then -- deaths
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pDeath[row-1])
elseif col == 7 then -- dmg
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pDmg[row-1])
elseif col == 8 then -- healing
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pHealing[row-1])
elseif col == 9 then -- absorbs
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pAbsorbed[row-1])
elseif col == 10 then -- clutch spells
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
BlzFrameSetText(fh, mvp_data.pClutch[row-1])
elseif col == 11 then -- score
fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
if mvp_data.mvpWinner == row-1 then -- mvp text color
BlzFrameSetText(fh, mvp_data.mvpTxtC .. mvp_data.pScore[row-1] .. '|r')
else
BlzFrameSetText(fh, mvp_data.pScore[row-1])
end
end
if col == 1 and row ~= 1 then -- hero icon move
BlzFrameSetPoint(fh,FRAMEPOINT_TOPLEFT,mvp_data.leaderboard_bd,FRAMEPOINT_TOPLEFT,
xOffset+(cell_width/2)-(cell_height/2)+cellPadding,yOffset)
else -- standard column
BlzFrameSetPoint(fh,FRAMEPOINT_TOPLEFT,mvp_data.leaderboard_bd,FRAMEPOINT_TOPLEFT,xOffset,yOffset)
end
if isIcon then
BlzFrameSetSize(fh,cell_height - cellPadding, cell_height - cellPadding)
isIcon = false
else
BlzFrameSetSize(fh,cell_width, cell_height)
end
BlzFrameSetVisible(fh, true)
BlzFrameSetAlpha(fh, 255)
BlzFrameSetTextSizeLimit(fh, 1)
if row == 1 then -- title color
BlzFrameSetText(fh, mvp_data.titleColor .. BlzFrameGetText(fh) .. '|r')
elseif col ~= 1 then -- row color
BlzFrameSetText(fh, mvp_data.textColors[col] .. BlzFrameGetText(fh) .. '|r')
end
BlzFrameSetTextAlignment(fh, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_CENTER)
end
end
BlzFrameSetSize(mvp_data.leaderboard_bd, lb_x + lb_xpad, lb_y + lb_ypad)
BlzFrameSetPoint(mvp_data.leaderboard_bd, FRAMEPOINT_CENTER, mvp_data.gameUI, FRAMEPOINT_CENTER, 0.0 - lb_xpad/2, lb_yoffset + lb_ypad/2)
end
-- non-local code:
CreateShowHideButton(pInt) -- show/hide btn
end
end
-- hide leaderboard:
-- mvp.ScoreboardShow(true)
end
-- generate a cell in the leaderboard table:
-- :: returns framehandle
function mvp.ScoreboardCreateFrame(pInt, col, row, frameType)
mvp_data.lb_frames[pInt].lb_table[col][row] = BlzCreateFrameByType(frameType, "lb-"..col.."-"..row, mvp_data.leaderboard_bd, "", 0)
return mvp_data.lb_frames[pInt].lb_table[col][row]
end
-- shift to next col:
function mvp.ScoreboardOffsetCalcX(col, cell_width, nudge)
if col > 1 then
return (cell_width * (col-1)) - nudge
else
return cell_width/3 * (col-1)
end
end
-- start a column and iterate down its rows:
function mvp.ScoreboardOffsetCalcY(row, cell_height)
return -(cell_height * (row-1))
end
-- create a line item for the UI component
function mvp.ScoreboardGenerateRow()
end
-- remove clutter from staging area
function mvp.ClearArea()
local g = CreateGroup()
GroupEnumUnitsInRange(g, 0, 0, 1250.0, Condition(function()
if not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) or IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) then
RemoveUnit(GetFilterUnit())
return true
end
end))
DestroyGroup(g)
end
-- hide heroes to prep for mvp sequence
function mvp.ShowHero(pInt, bool)
ShowUnit(udg_playerHero[pInt], bool)
if not bool then -- move somewhere out of the way
SetUnitX(udg_playerHero[pInt], mvp_data.y + 800)
SetUnitY(udg_playerHero[pInt], mvp_data.x - 800)
end
end
-- show clutch callout eyecandy
-- @unit = location of arcing text
-- @dBool = is it damage? (false for healing, true for damage)
function mvp.ArcingClutchText(unit, dBool)
if dBool then
ArcingTextTag('|cffff8505Clutch!|r', unit)
else
ArcingTextTag('|cff00b1ffClutch!|r', unit)
end
end
-- increase the damage score of a player
function mvp.IncrementDamage(pInt, val) -- 1-based pInt
if val < 5000 then -- don't increment if it's hacky damage e.g. instant kill
mvp_data.pDmg[pInt] = mvp_data.pDmg[pInt] + val
end
end
-- increase the healing score of a player
function mvp.IncrementHealing(pInt, val) -- 1-based pInt
if val < 0 then local val = -val end -- control for negatives i.e. healing via negative damage.
if val < 5000 then -- don't increment if it's hacky healing e.g. full heal
mvp_data.pHealing[pInt] = mvp_data.pHealing[pInt] + val
end
end
-- increase the absorbed damage score of a player
function mvp.IncrementAbsorbed(pInt, val) -- 1-based pInt
if val < 5000 then -- don't increment if it's hacky effect e.g. invul
mvp_data.pAbsorbed[pInt] = mvp_data.pAbsorbed[pInt] + val
end
end
-- increase the completed objectives of a player
function mvp.IncrementObjectiveCap(pInt, val) -- 1-based pInt
mvp_data.pObjCap[pInt] = mvp_data.pObjCap[pInt] + val
end
-- increase the clutch spell use events of a player
function mvp.IncrementClutch(pInt, val) -- 1-based pInt
mvp_data.pClutch[pInt] = mvp_data.pClutch[pInt] + val
end
-- if needed, map existing variables to mvp data (these GUI vars exist in my personal project as an example):
function mvp.MapVariableValues()
-- change the set variables to your map values e.g. udg_myHeroDeaths
for pInt = 1,10 do
mvp_data.pExp[pInt] = udg_mbXPEarned[pInt]
mvp_data.pElim[pInt] = udg_mbEliminations[pInt]
mvp_data.pDeath[pInt] = udg_mbDeaths[pInt]
end
-- ... add any others as needed (e.g. damage, healing, etc.)
end
-- create a backdrop and set it to be positioned at the desired frame via FRAMEPOINT_CENTER.
-- @fh = target this frame
-- @newFrameNameString = enter a custom value to manipulate backdrop by name if needed e.g. "myInventoryBackdrop"
-- @texturePathString = [optional; usually needed] texture (.blp) as the background
-- @alphaValue = [optional] if the backdrop should be transparent, pass in a value 0-255
-- @offsetx = [optional] nudge the frame left or right (percentage of screen)
-- @offsety = [optional] nudge the frame up or down (percentage of screen)
-- @width, @height = [optional] override the frame size
-- :: returns framehandle
function mvp.AttachBackdropByHandle(fh, newFrameNameString, texturePathString, alphaValue, offsetx, offsety, width, height)
local nh = BlzCreateFrameByType("BACKDROP", newFrameNameString, mvp_data.gameUI, "", 0)
local x = offsetx or 0.0
local y = offsety or 0.0
local w = width or BlzFrameGetWidth(fh)
local h = height or BlzFrameGetHeight(fh)
BlzFrameSetSize(nh, w, h)
BlzFrameSetPoint(nh, FRAMEPOINT_CENTER, fh, FRAMEPOINT_CENTER, 0.0 + x, 0.0 + y)
if texturePathString then
BlzFrameSetTexture(nh, texturePathString, 0, true)
end
BlzFrameSetLevel(nh, 0)
if alphaValue then
BlzFrameSetAlpha(nh, math.ceil(alphaValue))
end
return nh
end
-- callback when show/hide is clicked:
function ShowHideBtnClick(pInt)
if GetLocalPlayer() == Player(pInt-1) then
if not BlzFrameIsVisible(mvp_data.leaderboard_bd) then
mvp.ScoreboardShow(true)
BlzFrameSetText(mvp_data.showHideBtn[pInt], "Hide Leaderboard")
else
mvp.ScoreboardShow(false)
BlzFrameSetText(mvp_data.showHideBtn[pInt], "Show Leaderboard")
end
end
end
-- create button to show/hide the board:
function CreateShowHideButton(pInt)
local trig = CreateTrigger()
mvp_data.showHideBtn[pInt] = BlzCreateFrame("ScriptDialogButton", mvp_data.gameUI, 0.0, 0.0)
BlzFrameSetSize(mvp_data.showHideBtn[pInt], 0.13, 0.03)
BlzFrameSetPoint(mvp_data.showHideBtn[pInt], FRAMEPOINT_CENTER, mvp_data.leaderboard_bd, FRAMEPOINT_BOTTOM, 0.0, 0.0)
BlzFrameSetText(mvp_data.showHideBtn[pInt], "Hide Leaderboard")
BlzTriggerRegisterFrameEvent(trig, mvp_data.showHideBtn[pInt], FRAMEEVENT_CONTROL_CLICK)
TriggerAddAction(trig, function() ShowHideBtnClick(GetConvertedPlayerId(GetTriggerPlayer())) end)
-- hide btn for other players:
for i = 1,mvp_data.maxPlayers do
if GetLocalPlayer() == Player(i-1) and GetLocalPlayer() ~= Player(pInt-1) then
BlzFrameSetVisible(mvp_data.showHideBtn[pInt],false)
end
end
end
-- flatten values:
function FloorPlayerScoreValues()
for pInt = 1,10 do
mvp_data.pDmg[pInt] = math.floor(math.abs(mvp_data.pDmg[pInt]))
mvp_data.pHealing[pInt] = math.floor(math.abs(mvp_data.pHealing[pInt]))
mvp_data.pAbsorbed[pInt] = math.floor(math.abs(mvp_data.pAbsorbed[pInt]))
mvp_data.pElim[pInt] = math.floor(math.abs(mvp_data.pElim[pInt]))
mvp_data.pDeath[pInt] = math.floor(math.abs(mvp_data.pDeath[pInt]))
mvp_data.pExp[pInt] = math.floor(math.abs(mvp_data.pExp[pInt]))
mvp_data.pObjCap[pInt] = math.floor(math.abs(mvp_data.pObjCap[pInt]))
mvp_data.pClutch[pInt] = math.floor(math.abs(mvp_data.pClutch[pInt]))
mvp_data.pScore[pInt] = math.floor(math.abs(mvp_data.pScore[pInt]))
end
end
-- TriggerRegisterVariableEvent hook to convert these old school events into something more useful.
do
events = {}
function onRegisterVar(func)
events[func] = func
end
local oldEvent = TriggerRegisterVariableEvent
function TriggerRegisterVariableEvent(trig, var, op, val)
for k, func in pairs(events) do
if func(trig, var, val) then return end
end
oldEvent(trig, var, op, val)
end
end
-- :: helper function, safe counter
-- @t = table to return size of, returns int
function GetTableSize(t)
local count = 0
for k,v in pairs(t) do
count = count + 1
end
return count
end
-- :: helper function to remove table index by searching for value
-- @t = table to search
-- @v = value to search for, returns index of that value where it exists in @t, returns false if it doesn't exist
function GetTableIndexByValue(t,v)
for index, value in pairs(t) do
if value == v then
return index
end
end
return nil
end
-- :: helper function to see if table has value
-- @t = table to search
-- @v = value to search for, returns true if it exists; false if not
function DoesTableContainValue(t,v)
for index, value in pairs(t) do
if value == v then
return true
end
end
return false
end
-- :: debug helper; prevent permanent messages
-- @text = message (auto timed)
function DebugMsg(text)
DisplayTextToForce( bj_FORCE_ALL_PLAYERS, text )
end
-- :: debug helper; print array key value pairs
-- @t = table to print
function PrintArray(t)
for index, value in pairs(t) do
DebugMsg(index .. " has value " .. value)
end
end
-- @g = group to get size for, returns int (alternative: BlzGroupGetSize(g) )
function GetGroupSize(g)
local size = 0
local u
if (not IsUnitGroupEmptyBJ(g)) then
u = FirstOfGroup(g)
repeat
size = size + 1
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
until (u == nil or size > 32)
end
return size
end
-- :: get the host of the game and set it to a GUI var (run time elapsed trigger)
function GetHost()
local g = InitGameCache("Map.w3v")
StoreInteger ( g, "Map", "Host", GetPlayerId(GetLocalPlayer ())+1)
TriggerSyncStart ()
SyncStoredInteger ( g, "Map", "Host" )
TriggerSyncReady ()
udg_gameHostPlayer = Player( GetStoredInteger ( g, "Map", "Host" )-1)
FlushGameCache( g )
g = nil
end
function printTable(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. printTable(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
-- :: fetch x,y,angle from a set distance between point A and point B
-- @x1,y1 = origin coord
-- @x2,y2 = direction coord
-- @d = distance between origin and direction
function PointBetweenXY(x1,y1,x2,y2,d) -- credit: PurgeandFire https://www.hiveworkshop.com/threads/x-y-coordinates-and-facing.269871/
local angle = Atan2(y2 - y1, x2 - x1)
local x = x1 + d * Cos(angle)
local y = y1 + d * Sin(angle)
return x,y,angle
end
-- :: get facing angle from A to B
-- @x1,y1 = point A (facing from)
-- @x2,y2 = point B (facing towards)
function AngleBetweenPointsXY(x1,y1,x2,y2)
return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
end
-- :: (alternative name that always causes headaches, so make it work) get facing angle from A to B
-- @x1,y1 = point A (facing from)
-- @x2,y2 = point B (facing towards)
function AngleBetweenXY(x1,y1,x2,y2)
return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
end
-- :: PolarProjectionBJ converted to x,y
-- @x1,y1 = origin coord
-- @d = distance between origin and direction
-- @a = angle to project
function PolarProjectionXY(x1,y1,d,a)
local x = x1 + d * Cos(a * bj_DEGTORAD)
local y = y1 + d * Sin(a * bj_DEGTORAD)
return x,y
end
-- :: clones a table versus turning it into a pointer.
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
-- :: helper function to check if table has index value.
-- @t = table to search.
-- @v = value to search for, returns true if it exists; false if not.
function DoesTableContainIndex(t,v)
for index, value in pairs(t) do
if index == v then
return true
end
end
return false
end
function DistanceBetweenXY(x1,y1,x2,y2)
local dx = x2 - x1
local dy = y2 - y1
return SquareRoot(dx * dx + dy * dy)
end
function PrintFunctionError( func )
--local err = function() return 'error occurred' end
local status, err, ret = xpcall(func, err, 1, 1)
print(status)
print(err)
print(ret)
print('')
end
function tablelength(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
function GetHost()
local g = InitGameCache("Map.w3v")
StoreInteger(g, "Map", "Host", GetPlayerId(GetLocalPlayer ())+1)
TriggerSyncStart()
SyncStoredInteger(g, "Map", "Host" )
TriggerSyncReady()
udg_Host = Player( GetStoredInteger(g, "Map", "Host" )-1)
FlushGameCache(g)
g = null
end
function math.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
do
local data = {}
function SetTimerData(whichTimer, dat)
data[whichTimer] = dat
end
--GetData functionality doesn't even require an argument.
function GetTimerData(whichTimer)
if not whichTimer then whichTimer = GetExpiredTimer() end
return data[whichTimer]
end
--NewTimer functionality includes optional parameter to pass data to timer.
function NewTimer(...)
local t = CreateTimer()
local arg = {...}
data[t] = {}
if arg then
for i,v in ipairs(arg) do
data[t][i] = v
end
end
return t
end
--Release functionality doesn't even need for you to pass the expired timer.
--as an arg. It also returns the user data passed.
function ReleaseTimer(whichTimer)
if not whichTimer then whichTimer = GetExpiredTimer() end
local dat = data[whichTimer]
data[whichTimer] = nil
PauseTimer(whichTimer)
DestroyTimer(whichTimer)
return dat
end
end
-- :: increase str, agi or int for hero
-- @unit = hero
-- @attributeInt = 0 for Str, 1 for Agi, 2 for Int
-- @amountReal = amount to increase
function HeroAddStat(unit, attributeInt, amountInt)
ModifyHeroStat( attributeInt, unit, bj_MODIFYMETHOD_ADD, amountInt )
end
-- :: decrease str, agi or int for hero
-- @unit = hero
-- @attributeInt = 0 for Str, 1 for Agi, 2 for Int
-- @amountReal = amount to increase
function HeroRemoveStat(unit, attributeInt, amountInt)
ModifyHeroStat( attributeInt, unit, bj_MODIFYMETHOD_SUB, amountInt )
end
-- :: increase str, agi or int for hero over time (for items that increment)
-- @unit = hero
-- @attributeInt = 0 for Str, 1 for Agi, 2 for Int
-- @amountReal = amount to increase
-- @periodReal = how often stat is added (e.g. 60.0)
-- @ceilingInt = max amount of stat to add (e.g. 10)
function HeroAddStatTimer(unit, attributeInt, amountInt, periodReal, ceilingInt)
local i = 0
TimerStart(NewTimer(),periodReal,true,function()
ModifyHeroStat( attributeInt, unit, bj_MODIFYMETHOD_ADD, amountInt )
i = i + 1
if (i >= ceilingInt) then
ReleaseTimer()
end
end)
end
--
-- spell utils: do repetitive tasks with ease and return commonly-used local variables:
--
-- local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
-- :: returns caster,tarX,tarY
function SpellPackSetupTargetAbil()
local caster = GetTriggerUnit()
local tarX = GetSpellTargetX()
local tarY = GetSpellTargetY()
local casterX = GetUnitX(caster)
local casterY = GetUnitY(caster)
return caster, tarX, tarY, casterX, casterY
end
-- local caster, casterX, casterY = SpellPackSetupInstantAbil()
-- :: returns caster,tarX,tarY
function SpellPackSetupInstantAbil()
local caster = GetTriggerUnit()
local casterX = GetUnitX(caster)
local casterY = GetUnitY(caster)
return caster, casterX, casterY
end
-- local caster, tarX, tarY, target, casterX, casterY = SpellPackSetupTargetUnitAbil()
-- :: returns caster,tarX,tarY
function SpellPackSetupTargetUnitAbil()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local tarX = GetUnitX(target)
local tarY = GetUnitY(target)
local casterX = GetUnitX(caster)
local casterY = GetUnitY(caster)
return caster, tarX, tarY, target, casterX, casterY
end
-- move units away so the outpost is centered properly
-- @x,y = where to check
-- @radius = check this radius, then move units this distance from center
-- @ancientsBool = should this ignore objective units? (ancient unit type)
function SpellPackClearArea(x,y,radius,ancientsBool)
local g = CreateGroup()
local centerLoc = Location(x,y)
local r
if (radius) then r = radius else r = 335.0 end
if (ancientsBool ~= nil and ancientsBool) then
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitAliveBJ(GetFilterUnit())
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) end ) )
else
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitAliveBJ(GetFilterUnit())
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) end ) )
end
if (not IsUnitGroupEmptyBJ(g)) then
local loc
local newLoc
local u = FirstOfGroup(g)
local i = 0
local angle
repeat
loc = GetUnitLoc(u)
angle = AngleBetweenPoints(centerLoc, loc)
newLoc = PolarProjectionBJ(centerLoc, r, angle)
i = i + 1
SetUnitX(u,GetLocationX(newLoc))
SetUnitY(u,GetLocationY(newLoc))
RemoveLocation(loc)
RemoveLocation(newLoc)
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
until (u == nil or i == 64)
u = nil
end
DestroyGroup(g)
RemoveLocation(centerLoc)
end
--:: sets a unit's attack damage and health based on attribute values
-- @sourceHero = unit to read attributes from
-- @targetUnit = unit to increase stats for
-- @heroAttribute = 0,1,2 for Str, Agi, Int
-- @damageMult = base damage; multiplier for @attribute (e.g. 1.0 x Str)
-- @healthMult = [optional] health; multiplier for @attribute (e.g. 7.5 x Str)
function SpellPackSetUnitStats(sourceHero,targetUnit,heroAttribute,damageMult,healthMult)
if (healthMult ~= nil) then
BlzSetUnitMaxHP(unit,math.floor(healthMult*GetHeroStatBJ(heroAttribute, sourceHero, true)))
SetUnitState(targetUnit, UNIT_STATE_LIFE, math.ceil(BlzGetUnitMaxHP(targetUnit)))
end
local newdmg = math.floor(damageMult*GetHeroStatBJ(heroAttribute, sourceHero, true))
BlzSetUnitBaseDamage( targetUnit, newdmg, 0 )
end
--
-- util functions for setting up hero abilities:
--
-- @trigger = attach events and conditions to this trigger
-- @callback = function to run when conditions are met
-- @pInt = player number of trigger owner
-- @abilId = string raw code of ability being cast
-- @spellEventType = [optional] defaults to EVENT_PLAYER_UNIT_SPELL_EFFECT
function SpellPackCreateTrigger(trigger,callback,pInt,abilId,spellEventType)
local eventType
if (spellEventType ~= nil) then
eventType = spellEventType
else
eventType = EVENT_PLAYER_UNIT_SPELL_EFFECT
end
TriggerRegisterPlayerUnitEvent(trigger,Player(pInt - 1),eventType, nil)
TriggerAddCondition(trigger, Filter( function() return GetSpellAbilityId() == FourCC(abilId) end ) )
TriggerAddAction(trigger,callback)
end
-- @trigger = attach events and conditions to this trigger
-- @pInt = player number of trigger owner
-- @abilId = string raw code of ability being cast
-- @func = callback function to run when abilId is learned by player's hero
function SpellPackCreateLearnTrigger(trigger, pInt, abilId, func)
TriggerRegisterPlayerUnitEvent(trigger, Player(pInt - 1), EVENT_PLAYER_HERO_SKILL, nil)
TriggerAddCondition(trigger, Filter( function() return GetLearnedSkillBJ() == FourCC(abilId) end ) )
TriggerAddAction(trigger, function() func() end)
end
-- @trigger = attach events and conditions to this trigger
-- @callback = function to run when conditions are met
-- @pInt = player number of trigger owner
-- @boolexpr = conditions to be met for @trigger to run; default = source is udg_playerHero[pInt]
-- @realstring = [optional] real event to listen for (udg_DamageEvent, udg_DamageEventModifier, etc.); default = udg_DamageEvent
function SpellPackCreateDamageTrigger(trigger,callback,pInt,boolexpr,realstring)
local realS = "udg_DamageEvent"
local boolE
if (realstring ~= nil) then
realS = realstring
end
if (boolexpr ~= nil) then
boolE = boolexpr
else
boolE = function() return udg_DamageEventSource == udg_playerHero[pInt] end
end
TriggerRegisterVariableEvent( trigger, realS, EQUAL, 1.00 )
TriggerAddCondition(trigger, Filter( function() return
boolE()
and udg_DamageEventAmount > 1.0
end ) )
TriggerAddAction(trigger,callback)
end
-- @unit = unit casting the spell
-- @err = error text to display
function SpellPackInvalidMsg(unit,err)
DisplayTimedTextToPlayer(GetOwningPlayer(unit),0,0,1.5,'|cffffc800' .. tostring(err) .. '|r')
end
-- :: create a dummy that uses a native lightning spell for a special effect
-- @unit = [required] owner of the dummy; effect start location.
-- @abilityId = [optional] e.g. "A00A" (should be a lightning spell); defaults to chain lightning.
-- @orderString = [optional] order string to use ability on unit (defaults to 'chainlightning').
-- @targetUnit = [required] target of the ability; if nil, dummy casts on self aka @unit.
-- :: example terse use: SpellPackLightningEffect(myHero,nil,nil,myTarget)
function SpellPackLightningEffect(unit,abilityId,orderString,targetUnit)
local abilityId = abilityId or 'A075'
local orderString = orderString or 'chainlightning'
local x = GetUnitX(unit)
local y = GetUnitY(unit)
local d = DamagePackageCreateDummy(GetOwningPlayer(unit), 'e003', x, y)
UnitApplyTimedLifeBJ( 2.0, FourCC('BTLF'), d )
UnitAddAbility( d, FourCC(abilityId) )
IssueTargetOrder( d, orderString, targetUnit )
end
-- :: force an ability to be constrained to charge mechanics (on its X use, it will incur its cooldown)
-- @unit = casting unit
-- @abilId = abil rawcode (string)
-- @abilLevel = level of ability using 0-based index (0 is level 1)
-- @uses = total charges
-- @abilCooldown = triggered abil timer when @uses reached
-- @abilRefresh = when the charges reset by default
function SpellPackChargedAbilityInit(unit, abilId, abilLevel, uses, abilCooldown, abilRefresh)
local t = {}
t.unit = unit
t.uses = uses -- usable charges
t.consumed = 0 -- data that updates
t.abilId = FourCC(abilId)
t.abilLvl = abilLevel
t.abilCD = abilCooldown
t.refreshCD = abilRefresh
t.timer = NewTimer()
return t
end
-- :: queue ability to refresh its stacks if they aren't used
-- @t = chargedAbil object from SpellPackChargedAbility
function SpellPackChargedTimer(t)
ReleaseTimer(t.timer)
t.timer = NewTimer()
TimerStart(t.timer,t.refreshCD,false,function()
if t.consumed > 0 then
t.consumed = 0
-- remove the cooldown to allow uses until it is triggered:
BlzSetUnitAbilityCooldown( t.unit, t.abilId, t.abilLvl, 0.0 )
end
ReleaseTimer()
end)
end
-- :: run on use of a charged spell, consuming a charge and checking its refresh state
-- @t = chargedAbil object from SpellPackChargedAbility
function SpellPackChargedConsumed(t)
t.consumed = t.consumed + 1
if t.consumed >= t.uses then
BlzSetUnitAbilityCooldown( t.unit, t.abilId, t.abilLvl, t.abilCD )
BlzStartUnitAbilityCooldown( t.unit, t.abilId, t.abilCD )
else
SpellPackChargedTimer(t)
end
end
-- :: regenerate a charge and refresh the ability cooldown if needed
-- @t = chargedAbil object from SpellPackChargedAbility
function SpellPackChargedRefresh(t)
t.consumed = t.consumed - 1
if t.consumed < t.uses then
if t.consumed < 0 then
t.consumed = 0
end
BlzSetUnitAbilityCooldown( t.unit, t.abilId, t.abilLvl, 0.0 )
BlzEndUnitAbilityCooldown( t.unit, t.abilId )
else
SpellPackChargedTimer(t)
end
end
-- :: create a special effect with a parabolic trajectory
-- @x,y = originating location.
-- @x2,y2 = landing location.
-- @maxHeight = determines max vertex location.
-- @duration = trajectory completion time.
-- @effect = missile effect
-- @scale = scale of missile
-- @func = [optional] callback function when the projectile lands. should accept x and y as parameters for the landing location e.g. myFunc(x,y).
-- :: returns the generate special effect.
function SpellPackParabolicMissile(x,y,x2,y2,maxHeight,duration,effect,scale,func)
-- r(t) = (x(t), y(t), z(t))
-- can't figure out the math right now so it's just a triangle with derp math.
local d = DistanceBetweenXY(x,y,x2,y2)
local a = AngleBetweenXY(x,y,x2,y2)
local e = AddSpecialEffect(effect,x,y)
local dHalf = d/2 -- where we inverse the derp triangle.
local z = 0 -- starting and calc'd height.
local Vz = (maxHeight/(duration/0.03))*2 -- z velocity using derp math.
local Vx = d/(duration/0.03) -- x velocity using derp math.
local t = 0 -- traveled so far (velocity per cadence).
local x,y = x,y
BlzSetSpecialEffectScale(e,scale)
TimerStart(NewTimer(),0.03,true,function()
t = t + Vx
if t > dHalf then
z = z - Vz
else
z = z + Vz
end
x,y = PolarProjectionXY(x,y,Vx,a)
BlzSetSpecialEffectYaw(e, a*bj_DEGTORAD)
BlzSetSpecialEffectX(e, x)
BlzSetSpecialEffectY(e, y)
BlzSetSpecialEffectZ(e, z + GetTerrainCliffLevel(x,y)*128 - 256)
if t >= d then
if func then func(x2,y2) end
DestroyEffect(e)
ReleaseTimer()
end
end)
return e
end
-- @sourceUnit = unit to deal damage FROM
-- @targetUnit = unit to deal damage FOR
-- @amountReal = amount to deal
-- @attackTypeInt = leave as 0 for default or set attack type (Spell, Hero, etc. use damage engine vars e.g. udg_ATTACK_TYPE_HERO)
-- @damageTypeInt = leave as 0 for default or set damage type (Normal, etc. use damage engine vars e.g. udg_DAMAGE_TYPE_NORMAL)
-- @healBool = is the effect healing or damage (true for healing)
-- @effectString (optional) = the special effect to create at origin
-- @effectScaleReal (optional) = scale effect
-- @zReal (optional) = scale Z offset
-- :: note: Should no longer add arguments to this function since in GUI there is a custom script string cap
function DamagePackageDealDirect(sourceUnit, targetUnit, amountReal, attackTypeInt, damageTypeInt, healBool, effectString, effectScaleReal, zReal)
local aType = 0
local dType = 0
if (attackTypeInt ~= nil and attackTypeInt ~= 0) then
aType = attackTypeInt
else
aType = ATTACK_TYPE_NORMAL
end
if (damageTypeInt ~= nil and damageTypeInt ~= 0) then
dType = damageTypeInt
else
dType = DAMAGE_TYPE_NORMAL
end
if (healBool) then
mvp.IncrementHealing(GetConvertedPlayerId(GetOwningPlayer(sourceUnit)), amountReal)
SetUnitLifeBJ(targetUnit, GetWidgetLife(targetUnit) + amountReal )
ArcingTextTag( "|cff32ff32+" .. R2I(amountReal) .. "|r", targetUnit)
else
UnitDamageTargetBJ(sourceUnit,targetUnit,amountReal, aType, dType)
end
if (effectString ~= nil and effectScaleReal ~= nil and zReal ~= nil) then
DamagePackageSpecialEffect(targetUnit, effectString, effectScaleReal, zReal)
elseif (effectString ~= nil and effectScaleReal ~= nil) then
DamagePackageSpecialEffect(targetUnit, effectString, effectScaleReal, 1.0)
elseif (effectString ~= nil) then
DamagePackageSpecialEffect(targetUnit, effectString, 1.0, 0)
end
end
-- :: deal damage to enemy targets in a radius; or, set healBool to 'true' to heal allies
-- v1.0:
-- @sourceUnit = unit to deal damage FROM
-- @x,y = position to measure radius from
-- @rangeReal = area effect radius to check for
-- @amountReal = amount of damage to deal to target (pass in as var)
-- @healBool = is the effect healing or damage (true for healing, inverses damage amount)
-- @dummyBool = does the effect need to generate a spell dummy?
-- @dummyIdString = the spell id for the spell dummy (when set to true) to use on the damaged target e.g. 'e00d'
-- @dummyString = the order string for the spell dummy to use on the picked target
-- @effectString = optional string to use to create a special effect at unit position (feet)
-- @effectScaleReal = optional scale real (e.g. 0.50)
-- v2.0:
-- (** CANNOT BE RE-ORDERED IN FRONT OF v1.0 TO KEEP BACKWARDS-COMPATIBLE***)
-- @structureBool = should the ability damage structures
-- @excludeUnit = optional unit to exclude from damage (i.e. for abilities that have a bonus AoE effect when target is damaged)
function DamagePackageDealAOE(sourceUnit, x, y, rangeReal, amountReal, healBool, dummyBool, dummyIdString, dummyString, effectString, effectScaleReal, structureBool, excludeUnit)
local g = CreateGroup()
local i = 0
local aType = ATTACK_TYPE_NORMAL
local p = GetOwningPlayer(sourceUnit)
local u
if (healBool) then -- set to negative to inflict healing
GroupEnumUnitsInRange(g, x, y, rangeReal, Condition( function()
if DamagePackageAllyFilter(GetFilterUnit(), p) then
mvp.IncrementHealing(GetConvertedPlayerId(GetOwningPlayer(sourceUnit)), amountReal)
return true
end
end ) )
elseif (structureBool) then
GroupEnumUnitsInRange(g, x, y, rangeReal, Condition( function() return DamagePackageEnemyAndStructureFilter(GetFilterUnit(), p) end ) )
else
GroupEnumUnitsInRange(g, x, y, rangeReal, Condition( function() return DamagePackageEnemyFilter(GetFilterUnit(), p) end ) )
end
if (not IsUnitGroupEmptyBJ(g)) then
u = FirstOfGroup(g)
if (excludeUnit ~= nil) then
GroupRemoveUnit(g, excludeUnit)
end
repeat
i = i + 1
if (effectString ~= nil and effectString ~= '') then
local loc = GetUnitLoc(u)
local effect = AddSpecialEffectLoc(effectString, loc)
BlzSetSpecialEffectScale( effect, effectScaleReal )
DestroyEffect(effect)
RemoveLocation(loc)
end
if (healBool) then
SetUnitLifeBJ(u, GetWidgetLife(u) + amountReal )
ArcingTextTag( "|cff32ff32+" .. R2I(amountReal) .. "|r", u)
else
UnitDamageTargetBJ(sourceUnit,u,amountReal, aType, DAMAGE_TYPE_NORMAL)
end
if (dummyBool) then
local loc2 = GetUnitLoc(u)
local d = DamagePackageCreateDummy(p, 'e003', x, y)
UnitApplyTimedLifeBJ( 1.75, FourCC('BTLF'), d )
UnitAddAbility( d, FourCC(dummyIdString) )
IssueTargetOrder( d, dummyString, u )
RemoveLocation(loc2)
end
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
until (u == nil or i == 36) -- prevent infinite loop due to reforged handle ID bug
end
DestroyGroup(g)
end
-- :: damage a target over time
-- @unit = unit that owns damage
-- @target = unit that takes damage
-- @amount = amount of damage to deal per interval
-- @duration = how long the effect lasts
-- @effect = special effect each interval
-- @timerPeriod = [optional] override how often damage is dealt (default = 1 sec)
-- @delayedBool = [optional] override timer type (e.g. conver to a delayed damage effect)
function DamagePackageDealOverTime(unit, target, amount, duration, effect, timerPeriod, periodicBool)
local i = 0
local cadence = timerPeriod or 1.0
local isPeriodic = periodicBool or true
TimerStart(NewTimer(), cadence, isPeriodic, function()
if IsUnitAliveBJ(target) and isPeriodic then
UnitDamageTargetBJ(unit, target, math.floor(amount), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
DestroyEffect(AddSpecialEffect(effect, GetUnitX(target), GetUnitY(target)))
i = i + 1
if i >= duration then ReleaseTimer() end
else
ReleaseTimer()
end
end)
end
-- :: filter for enemy targets, default with no structures
-- @unit - unit to filter
-- @player - is unit enemy of this player
function DamagePackageEnemyFilter(unit, player)
return IsUnitEnemy(unit,player)
and (not IsUnitDeadBJ(unit))
and (not IsUnitType(unit,UNIT_TYPE_STRUCTURE))
and (not IsUnitType(unit,UNIT_TYPE_MAGIC_IMMUNE))
end
-- :: filter for enemy targets, allow structures
-- @unit - unit to filter
-- @player - is unit enemy of this player
function DamagePackageEnemyAndStructureFilter(unit, player)
return IsUnitEnemy(unit,player)
and (not IsUnitDeadBJ(unit))
and (not IsUnitType(unit,UNIT_TYPE_MAGIC_IMMUNE))
end
-- :: filter for enemy targets, no summons
-- @unit - unit to filter
-- @player - is unit enemy of this player
function DamagePackageEnemyNonSummonFilter(unit, player)
return IsUnitEnemy(unit,player)
and (not IsUnitDeadBJ(unit))
and (not IsUnitType(unit,UNIT_TYPE_MAGIC_IMMUNE))
and (not IsUnitType(unit,UNIT_TYPE_SUMMONED))
end
-- :: filter for allied targets
-- @unit - unit to filter
-- @player - is unit enemy of this player
function DamagePackageAllyFilter(unit, player)
return not IsUnitEnemy(unit,player)
and (not IsUnitDeadBJ(unit))
and (not IsUnitType(unit,UNIT_TYPE_STRUCTURE))
end
-- :: create a dummy and have it cast a spell at a location
-- @sourceUnit = owner of dummy
-- @dummyIdString = the spell id for the spell dummy (when set to true) to use on the damaged target e.g. 'e00d'
-- @dummyOrderString = the order string for the spell dummy to use on the picked target
-- @damageAmount = optional; if the dummy should have its damage overridden by a new value, set this value to that amount
-- @x,y = optional; location where the dummy is created
-- @x2,y2 = optional; location to cast dummy effect (set to 0 or leave empty if it is insant/stomp)
-- @overrideDuration = optional; if the dummy effect lasts longer than 3.0 sec (e.g. very long missile), set a custom duration for the damage override listener to exist
function DamagePackageDummyAoE(sourceUnit, dummyIdString, dummyOrderString, damageAmount, x, y, x2, y2, overrideDuration)
local d
if (x == nil or y == nil) then
local defaultX = GetUnitX(sourceUnit)
local defaultY = GetUnitY(sourceUnit)
d = DamagePackageCreateDummy(GetOwningPlayer(sourceUnit), 'e003', defaultX, defaultY)
else
d = DamagePackageCreateDummy(GetOwningPlayer(sourceUnit), 'e003', x, y)
end
if (damageAmount ~= nil) then
local trig = CreateTrigger()
local dur = 3.0
local func = function()
udg_DamageEventAmount = damageAmount
end
if (overrideDuration ~= nil and overrideDuration > 0) then
dur = overrideDuration
end
TriggerRegisterVariableEvent( trig, "udg_DamageModifierEvent", EQUAL, 1.00 )
TriggerAddCondition(trig,Filter( function() return udg_DamageEventSource == d and udg_DamageEventAmount < 2.00 and udg_DamageEventAmount > 0
and udg_IsDamageSpell end))
TriggerAddAction(trig,func)
TimerStart(NewTimer(trig),dur,false,function()
DestroyTrigger(trig)
ReleaseTimer()
end)
end
UnitApplyTimedLifeBJ( 1.75, FourCC('BTLF'), d )
UnitAddAbilityBJ( FourCC(dummyIdString), d )
if (x == nil or y == nil or x2 == nil or y2 == nil) then
IssueImmediateOrder ( d, dummyOrderString )
else
IssuePointOrder ( d, dummyOrderString, x2, y2 )
end
end
-- :: create a dummy that casts an ability on self or target
-- @unit = owner of the dummy
-- @abilityId = e.g. "A00A"
-- @orderString = order string to use ability on unit
-- @targetUnit = (optional) target of the ability; if nil, dummy casts on self aka @unit
function DamagePackageDummyTarget(unit,abilityId,orderString,targetUnit)
local x = GetUnitX(unit)
local y = GetUnitY(unit)
local d = DamagePackageCreateDummy(GetOwningPlayer(unit), 'e003', x, y)
UnitApplyTimedLifeBJ( 0.75, FourCC('BTLF'), d )
UnitAddAbility( d, FourCC(abilityId) )
if (targetUnit == nil) then
IssueTargetOrder( d, orderString, unit )
else
SetUnitX(d,GetUnitX(targetUnit))
SetUnitY(d,GetUnitY(targetUnit))
IssueTargetOrder( d, orderString, targetUnit )
end
end
-- :: create a special effect at origin of unit
-- @unit = location of this unit
-- @effectString = effect to create (escape it e.g. \ becomes \\)
-- @scale (optional) = default 1.0, increase scale to this real amount
-- @z (optional) = increase Z of effect by real amount; defaults to 0
function DamagePackageSpecialEffect(unit, effectString, effectScaleReal, zReal)
local x = GetUnitX(unit)
local y = GetUnitY(unit)
local z = BlzGetUnitZ(unit)
local effect = AddSpecialEffect(effectString, x, y)
if (effectScaleReal ~= nil) then
BlzSetSpecialEffectScale(effect, effectScaleReal)
end
if (zReal ~= nil) then
BlzSetSpecialEffectZ(effect, z + zReal)
else
BlzSetSpecialEffectZ(effect, z)
end
DestroyEffect(effect)
end
-- :: add mana to a unit
-- @unit = unit to add mana to
-- @amountReal = amount to restore
-- @bool = (optional) is the amount a percentage? e.g. 3 for 3%
function DamagePackageAddMana(unit,manaAmount,bool)
local tag = ""
if (manaAmount > 0) then tag = "+" end
if (bool) then -- percent
local mana = GetUnitState(unit,UNIT_STATE_MANA)
local newVal = BlzGetUnitMaxMana(unit)*(manaAmount/100)
mana = math.ceil(mana + newVal)
SetUnitState( unit, UNIT_STATE_MANA, mana )
ArcingTextTag( "|cff0064ff" .. tag .. R2I(newVal) .. "|r", unit)
else -- raw value
SetUnitState( unit, UNIT_STATE_MANA, GetUnitState(unit, UNIT_STATE_MANA) + manaAmount )
ArcingTextTag( "|cff0064ff" .. tag .. R2I(manaAmount) .. "|r", unit)
end
end
-- :: add health to a unit
-- @healUnit = unit to add health to
-- @amountReal = amount to restore
-- @percentBool = optional; is the amount a percentage? e.g. 3 for 3%
-- @effect = optional; special effect to play (leave empty for default effect, pass empty string e.g. '' or '_' for no effect)
-- @sourceUnit = optional; unit to award MVP score for healing
function DamagePackageRestoreHealth(healUnit,healthAmount,percentBool,effectString,sourceUnit)
if healthAmount > 0 then
local sourceUnit = sourceUnit or nil
if not sourceUnit then -- attempt to catch general uses of spells if source is empty.
sourceUnit = GetTriggerUnit()
end
local life = GetUnitState(healUnit,UNIT_STATE_LIFE)
local scale = 1.0
local str = effectString or 'Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl'
local newVal
if percentBool then -- percent
newVal = BlzGetUnitMaxHP(healUnit)*(healthAmount/100)
life = math.ceil(life + newVal)
SetUnitState( healUnit, UNIT_STATE_LIFE, life )
if newVal >= 1 then
ArcingTextTag( "|cff32ff32+" .. R2I(newVal) .. "|r", healUnit)
end
if sourceUnit then DamagePackageHealingScore(sourceUnit, newVal) end
else -- raw value
life = math.ceil(life + healthAmount)
SetUnitState( healUnit, UNIT_STATE_LIFE, life )
ArcingTextTag( "|cff32ff32+" .. R2I(healthAmount) .. "|r", healUnit)
if sourceUnit then DamagePackageHealingScore(sourceUnit, healthAmount) end
end
if str ~= '' and str ~= '_' then
DestroyEffect(AddSpecialEffect(str,GetUnitX(healUnit), GetUnitY(healUnit)))
end
end
end
function DamagePackageHealingScore(u, a)
mvp.IncrementHealing(GetConvertedPlayerId(GetOwningPlayer(u)), a)
end
function BasicDamageExpr(p)
return IsUnitEnemy(GetFilterUnit(),p)
and IsUnitAliveBJ(GetFilterUnit())
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE))
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE))
end
-- :: create a dummy without indexing it
-- facing = [optional] facing angle of dummy
-- dummyDuration = [optional] how long it persists (seconds)
-- returns unit
function DamagePackageCreateDummy(dummyPlayer, dummyId, dummyX, dummyY, facing, dummyDuration)
local f
local dur
if (facing ~= nil) then f = math.floor(facing) else f = 270.0 end
udg_UnitIndexerEnabled = false
local dummyUnit = CreateUnit( dummyPlayer, FourCC(dummyId), dummyX, dummyY, f )
udg_UnitIndexerEnabled = true
if (dummyDuration ~= nil and dummyDuration > 0) then
dur = dummyDuration
else
dur = 1.75
end
UnitApplyTimedLifeBJ( dur, FourCC('BTLF'), dummyUnit )
return dummyUnit
end
--
-- group utils: functions to loop through groups to act on member units
--
LUA_FILTERUNIT = nil -- global filter unit that surplants blizzard.j filter unit
-- @g = group to loop through
-- @callback = function to run on each unit
function GroupUtilsAction(g,callback)
local size = BlzGroupGetSize(g)
if (size > 0) then
local i = 0
repeat
LUA_FILTERUNIT = BlzGroupUnitAt(g,i)
callback()
i = i + 1
until (i >= size or LUA_FILTERUNIT == nil)
end
end
--
-- group actions: variants that insert specific features into group loops
--
-- @g = group to check
-- @unit = remove this unit from group
function GroupActionRemoveDead(g)
GroupUtilsAction(g,function()
if (not IsUnitAliveBJ(LUA_FILTERUNIT)) then
GroupRemoveUnit(g,LUA_FILTERUNIT)
end
end)
end
-- @unit = order this unit to attack @targetUnit
-- @targetUnit = unit to attack
function GroupActionAttackUnit(g,targetUnit)
GroupUtilsAction(g,function()
if (IsUnitAliveBJ(LUA_FILTERUNIT)) then
IssueTargetOrder(LUA_FILTERUNIT,'attack',targetUnit)
end
end)
end
-- drafted here: https://docs.google.com/spreadsheets/d/1OPFIguwadVlQnwkL4NPSZeXRrfWzFUMdKMeEonDlAAc/edit#gid=0
-- init LB
function LeaderboardGenerate()
--config
local concatLength = 24 -- number of chars to limit player names to (prevent overlap)
local widthName = 12.33
local widthElims = 5.21
local widthDeaths = 6.12
local widthXP = 7.18
--
udg_mb = CreateMultiboard()
local rowSkip = 2 -- how many line items to skip to update a row (horde = 2, alliance = 3) aka unused line items to skip over
MultiboardSetTitleText(udg_mb,"Leaderboard")
MultiboardSetColumnCount(udg_mb,5)
MultiboardSetRowCount(udg_mb,13)
for col = 1,5 do -- go through each column
rowSkip = 2
for row = 1,13 do -- go through each row
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false) -- by default, hide cell
if (col == 1) then -- set column 1 values, etc.
if (row ~= 1 and row ~= 2 and row ~= 8) then -- set player hero icon placeholder
if (row > 8) then rowSkip = 3 else rowSkip = 2 end
if (udg_gamePlayerEmptyComp[GetConvertedPlayerId(ConvertedPlayer(row-rowSkip))] == true) then -- if player absent
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false) -- hide all
MultiboardSetItemIconBJ(udg_mb,col,row,'_')
else
MultiboardSetItemStyleBJ(udg_mb,col,row,false,true) -- hide values, show icons
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp') -- set icons
end
elseif (row == 1) then -- set header
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
elseif (row == 2) then -- hide title row details
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
elseif (row == 8) then -- hide title row details
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
end
MultiboardSetItemWidthBJ(udg_mb,col,row,1.25) -- set width for whole column
elseif (col == 2) then
if (row ~= 1 and row ~= 2 and row ~= 8) then
MultiboardSetItemStyleBJ(udg_mb,col,row,true,false) -- show values, hide icons
if (row > 8) then rowSkip = 3 else rowSkip = 2 end -- set up row skip to get over empty line items (1,2,8 are headers/empty)
if (udg_gamePlayerEmptyComp[GetConvertedPlayerId(ConvertedPlayer(row-rowSkip))] == true) then -- if player absent
MultiboardSetItemValueBJ(udg_mb,col,row,' ') -- hide player name
else
if (row <= 8) then -- horde color coded names
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffff2c2c' .. string.sub(GetPlayerName(ConvertedPlayer(row-rowSkip)),1,concatLength) .. '|r') -- set player name
else -- alliance color coded names
MultiboardSetItemValueBJ(udg_mb,col,row,'|cff2c2ce9' .. string.sub(GetPlayerName(ConvertedPlayer(row-rowSkip)),1,concatLength) .. '|r') -- set player name
end
end
elseif (row == 1) then -- set header
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
elseif (row == 2) then -- set title row Horde
MultiboardSetItemStyleBJ(udg_mb,col,row,true,true)
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNOrcCaptureFlag.blp')
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffffc800' .. GetPlayerName(ConvertedPlayer(13)) .. '|r') -- set player name
elseif (row == 8) then -- set title row Alliance
MultiboardSetItemStyleBJ(udg_mb,col,row,true,true)
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNHumanCaptureFlag.blp')
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffffc800' .. GetPlayerName(ConvertedPlayer(14)) .. '|r') -- set player name
end
MultiboardSetItemWidthBJ(udg_mb,col,row,widthName) -- set width for whole column
elseif (col == 3 or col == 4 or col == 5) then
if (row ~= 1 and row ~= 2 and row ~= 8) then -- set player hero icon placeholder
MultiboardSetItemStyleBJ(udg_mb,col,row,true,false) -- show values, hide icons
MultiboardSetItemValueBJ(udg_mb,col,row,'') -- initialize default value
elseif ((row == 1 and col == 3)) then -- set Eliminations header
MultiboardSetItemStyleBJ(udg_mb,col,row,true,true)
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNSteelMelee.blp')
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffffc800Kills|r')
elseif ((row == 1 and col == 4)) then -- set Deaths header
MultiboardSetItemStyleBJ(udg_mb,col,row,true,true)
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNCancel.blp')
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffffc800Deaths|r')
elseif ((row == 1 and col == 5)) then -- set XP header
MultiboardSetItemStyleBJ(udg_mb,col,row,true,true)
MultiboardSetItemIconBJ(udg_mb,col,row,'ReplaceableTextures\\CommandButtons\\BTNRallyPoint.blp')
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffffc800XP Earned|r')
elseif ((row == 2 and col == 3) or (row == 8 and col == 3)) then -- set faction Eliminations default value
MultiboardSetItemStyleBJ(udg_mb,col,row,true,false)
MultiboardSetItemValueBJ(udg_mb,col,row,'')
elseif ((row == 2 and col == 4) or (row == 8 and col == 4)) then -- set faction Deaths default value
MultiboardSetItemStyleBJ(udg_mb,col,row,true,false)
MultiboardSetItemValueBJ(udg_mb,col,row,'')
elseif ((row == 2 and col == 5) or (row == 8 and col == 5)) then -- set faction XP default value
MultiboardSetItemStyleBJ(udg_mb,col,row,true,false)
MultiboardSetItemValueBJ(udg_mb,col,row,'|cffc800c8Level 1|r')
elseif (row == 2) then -- hide title row details
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
elseif (row == 8) then -- hide title row details
MultiboardSetItemStyleBJ(udg_mb,col,row,false,false)
end
if (col == 3) then -- eliminations width
MultiboardSetItemWidthBJ(udg_mb,col,row,widthElims)
elseif (col == 4) then -- deaths width
MultiboardSetItemWidthBJ(udg_mb,col,row,widthDeaths)
elseif (col == 5) then -- xp earned width
MultiboardSetItemWidthBJ(udg_mb,col,row,widthXP)
end
end
end
end
MultiboardDisplay(udg_mb,true)
MultiboardMinimize(udg_mb,true)
end
-- split xp by nearby heroes and add to leaderboard stat (this gets called in Global Experience script)
function LeaderboardAddEligibleXP(x,y,xp,p)
local g = CreateGroup()
local pInt = GetConvertedPlayerId(p)
local size = 1
GroupEnumUnitsInRange(g, x, y, 1000.0, Condition( function() return DamagePackageAllyFilter(GetFilterUnit(), p) and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) and GetConvertedPlayerId(GetOwningPlayer(GetFilterUnit())) <= 10 end ) )
size = BlzGroupGetSize(g)
local u = FirstOfGroup(g)
local i = 0
local ownInt = pInt
if (not IsUnitGroupEmptyBJ(g)) then
repeat
ownInt = GetConvertedPlayerId(GetOwningPlayer(u))
udg_mbXPEarned[ownInt] = udg_mbXPEarned[ownInt] + (xp/size)
LeaderboardUpdateXP(ownInt)
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
i = i+1
until (u == nil or i > 10)
end
DestroyGroup(g)
end
-- update leaderboard player hero icon to unit icon
function LeaderboardUpdateHeroIcon(pInt)
if (pInt <= 10) then
local rowSkip = LeaderboardGetRowSkip(pInt)
if (udg_mbHeroIcon[pInt]) then
MultiboardSetItemStyleBJ(udg_mb,1,rowSkip+pInt,false,true)
MultiboardSetItemIconBJ(udg_mb,1,rowSkip+pInt,udg_mbHeroIcon[pInt])
end
end
end
-- update leaderboard player hero icon to a skull when they are dead
function LeaderboardUpdateDeathIcon(pInt)
if (pInt <= 10) then
local rowSkip = LeaderboardGetRowSkip(pInt)
if (udg_mbHeroIcon[pInt]) then
MultiboardSetItemIconBJ(udg_mb,1,rowSkip+pInt,'ReplaceableTextures\\CommandButtons\\BTNAnkh.blp')
end
end
end
-- update leaderboard values
function LeaderboardUpdateEliminations(pInt)
if (pInt <= 10) then
local rowSkip = LeaderboardGetRowSkip(pInt)
MultiboardSetItemValueBJ(udg_mb,3,rowSkip+pInt,udg_mbEliminations[pInt]) -- update player
end
local str = ''
str = '|cffffc800' .. udg_mbElimTotal[0] .. '|r'
MultiboardSetItemValueBJ(udg_mb,3,2,str) -- update faction total horde
str = '|cffffc800' .. udg_mbElimTotal[1] .. '|r'
MultiboardSetItemValueBJ(udg_mb,3,8,str) -- update faction total alliance
end
-- update leaderboard values
function LeaderboardUpdateDeaths(pInt)
if (pInt <= 10) then
local rowSkip = LeaderboardGetRowSkip(pInt)
MultiboardSetItemValueBJ(udg_mb,4,rowSkip+pInt,udg_mbDeaths[pInt])
end
local str = ''
str = '|cffffc800' .. udg_mbDeathsTotal[0] .. '|r'
MultiboardSetItemValueBJ(udg_mb,4,2,str) -- update faction total horde
str = '|cffffc800' .. udg_mbDeathsTotal[1] .. '|r'
MultiboardSetItemValueBJ(udg_mb,4,8,str) -- update faction total alliance
end
-- update leaderboard values
function LeaderboardUpdateXP(pInt)
if (pInt <= 10) then
local rowSkip = LeaderboardGetRowSkip(pInt)
MultiboardSetItemValueBJ(udg_mb,5,rowSkip+pInt,math.floor(udg_mbXPEarned[pInt]))
end
end
-- update leaderboard values
function LeaderboardUpdateFactionLevel()
MultiboardSetItemValueBJ(udg_mb,5,2,'|cffc800c8Level ' .. GetHeroLevel(udg_zGlobalXPUnit[0]) .. '|r')
MultiboardSetItemValueBJ(udg_mb,5,8,'|cffc800c8Level ' .. GetHeroLevel(udg_zGlobalXPUnit[1]) .. '|r')
end
-- run when a player leaves the game
function LeaderboardPlayerLeft(pInt)
local rowSkip = LeaderboardGetRowSkip(pInt)
MultiboardSetItemValueBJ(udg_mb,2,rowSkip+pInt,'|cffc0c0c0<left game>|r')
end
-- run to update player name; if colorString is empty or left nil then defaults to faction colors
function LeaderboardUpdatePlayerName(pInt, newNameString, colorString, isBotBool)
--if (LUA_VAR_CACHED_LB_NAME == nil) then
-- LUA_VAR_CACHED_LB_NAME = {}
--end
local rowSkip = LeaderboardGetRowSkip(pInt)
newNameString = string.sub(newNameString,1,24) -- prevent overlapping columns
if (colorString == nil) then
if pInt > 5 then -- alliance
colorString = '|cff2c2ce9'
else -- horde
colorString = '|cffff2c2c'
end
end
if (isBotBool) then
newNameString = colorString .. newNameString .. ' (|r|cff00e9ffBot|r' .. colorString .. ')|r'
else
newNameString = colorString .. newNameString .. '|r'
end
MultiboardSetItemValueBJ(udg_mb,2,rowSkip+pInt, newNameString )
--LUA_VAR_LB_CACHED_NAME[pInt] = newNameString
end
-- get line item to update based on player number (in order to skip non-player rows)
function LeaderboardGetRowSkip(pInt)
local rowSkip = 2
if (pInt > 5) then rowSkip = 3 end
return rowSkip
end
-- kept as a separate function on a timer for peformance
function UpdateExperience()
local hero
local xp = 0
for i = 1,10,1 do
hero = udg_playerHero[i]
if hero ~= nil then
if i <= 5 then
xp = GetHeroXP(udg_zGlobalXPUnit[0])
else
xp = GetHeroXP(udg_zGlobalXPUnit[1])
end
SetHeroXP(hero, xp, true)
end
end
end
-- run at map init to begin collating exp
function InitUpdateExperience()
globalExperienceTimer = NewTimer()
TimerStart(globalExperienceTimer,2.33,true,function()
UpdateExperience() end)
end
-- @slayer unit = award xp to this unit's faction unit
-- @victim unit = award xp based on this unit's type
-- returns true if experience was added, false if failed to add
function AddExperience(slayer, victim)
local slayerOwner
local victimOwner
local factionUnit
local xp = 0
local loc
local loopstart
local loopend
local x
local y
slayerOwner = GetOwningPlayer(slayer)
victimOwner = GetOwningPlayer(victim)
if ( IsPlayerAlly(slayerOwner, victimOwner) ) then
return false
else
loc = GetUnitLoc(victim)
x = GetLocationX(loc)
y = GetLocationY(loc)
-- force [0] = horde // [1] = alliance
if ( IsPlayerInForce( slayerOwner, udg_playerForce[0] ) ) then
factionUnit = udg_zGlobalXPUnit[0]
loopstart = 1
loopend = 5
elseif ( IsPlayerInForce( slayerOwner, udg_playerForce[1] ) ) then
factionUnit = udg_zGlobalXPUnit[1]
loopstart = 6
loopend = 10
end
if ( not IsUnitType( victim, UNIT_TYPE_SUMMONED ) and XPIsHeroInRange( loc, loopstart, loopend ) ) then
if ( IsUnitType( victim, UNIT_TYPE_HERO ) ) then
xp = udg_zGlobalXPVal*3 -- R2I(6 + ((GetHeroLevel(victim)*2)/GetHeroLevel(slayer))*6) -- award more experience to heroes that are a lower level than the hero slain
if (GetHeroLevel(victim) > GetHeroLevel(slayer)) then
xp = R2I(xp*1.5)
end
else
xp = udg_zGlobalXPVal
end
AddHeroXP(factionUnit, xp, true)
--ArcingTextTag( "|cffaa00aa+" .. R2I(xp) .. " xp|r", slayer )
LeaderboardAddEligibleXP(x,y,xp,slayerOwner)
end
RemoveLocation(loc)
return true
end
end
-- @point = position of victim
-- @loopstart = declares start for loop to simplify loop check (performance)
-- @loopend = declares end for loop to simplify loop check (performance)
-- return boolean
function XPIsHeroInRange(point, loopstart, loopend)
local bool = false
local loc = point
for i = loopstart,loopend,1 do
if ( IsUnitInRangeLoc( udg_playerHero[i], loc, 1000.0 ) ) then
bool = true
break
end
end
RemoveLocation(loc)
return bool
end
function DAI__VarGen()
-- [[ global agent config variables ---------------]]
-- general config:
ai__lowHealth__r = 30.0 -- if the agent reaches this percentage of health, they will retreat to heal at their regen point
ai__safeHealth__r = 75.0 -- if the agent reaches this percentage of health, they will resume lane activity
-- wave push settings:
ai__isSafeToPushRange__r = 2150.0 -- searches this radius for an allied group of units (minion wave) and commands the agent to follow it if count > ai__feelsSafeThreshold__i
ai__isSafeRange__r = 2000.0 -- determines the radius for an agent feeling safe (casting teleport, hold position at regen fountain, etc.)
ai__teleportRegenRange__r = 0.0 -- (set to 0.0 to always teleport) if the agent is at least this far away from a regen point, the agent will teleport to base instead of running around the map
ai__isNearEnemyHeroRange__r = 675.0 -- search this range to engage enemy heroes
ai__isNearAllyHeroRange__r = 1500.0 -- search this range to assist ally heroes
ai__isNearEnemyMinionRange__r = 650.0 -- search this range to engage minion waves
ai__isNearEnemyMinionCount__i = 3 -- if at least this nearby enemy minions are nearby, engage in WAVECLEAR
ai__followWaveLeashRange__r = 750.0 -- minimum follow distance before returning to an assigned wave during PUSH state
ai__feelsSafeThreshold__i = 1 -- the number of nearby allied minions required for an agent to begin pushing forward; if less than this, they retreat to focus point
-- combat settings:
ai__combatAttemptsAllowed = 7 -- determines how many abilities are used in COMBAT state (this * ai__combatAbilityCadence__r = COMBAT duration)
ai__combatFatigueDurationD__r = 1.13 -- the default setting for how long should the agent wait before entering COMBAT state again
ai__combatAbilityCadenceD__r = 0.99 -- the default setting for how often the a.i. should cycle through abilities while in COMBAT state
ai__combatDisengageHealthPerc__r = 22.5 -- if the agent loses this much % of their health after engaging, COMBAT ends early
ai__combatFleeDuration__r = 2.33 -- how long the agent should flee when hit by an objective spell or enemy tower-
ai__isNearTowersRange__r = 1500.0 -- determines if the agent is near an enemy base and prevents things like teleporting to home insie an enemy fortification
-- objective settings:
ai__combatFatigueDurationObj__r = 0.11 -- fatigue when objective is active
ai__combatAbilityCadenceObj__r = 3.33 -- cadence when objective is active
ai__objCenterBasesReq__i = 2 -- (0-based, 2 == 3 required) if number of bases owned by faction in center lane is less than this amount, ignore objective
-- misc. config:
ai__neutralOdds__i = 3 -- 1 in x chance for agent to select a neutral hero instead of a faction hero
ai__laneConfig__t = { -- set the number of heroes to assign to each lane by default (TODO: until we get more intelligent about reassignment)
[0] = 2,
[1] = 1,
[2] = 2
}
-- do not edit past this point:
ai__combatFatigueDuration__r = ai__combatFatigueDurationD__r -- initialize default
ai__combatAbilityCadence__r = ai__combatAbilityCadenceD__r -- initialize default
-- [[ ---------------------------------------------]]
-- [[ global agent data ----------------------------]]
-- udg_playerHero[n] == the agent's hero unit
ai__ignoreObj = {} -- if eligible, ignore objective when active
ai__combatStartingHealthPerc = {} -- percentage of life when entering combat
ai__playerNum = {} -- player slot the agent filled
ai__factionNum = {} -- assigned faction (0 = horde, 1 = alliance)
ai__state = {} -- the current state of the agent
ai__combatState = {} -- an intermediate state that determines what the agent should do in short windows of time
ai__combatFatigue = {} -- bool to control if the agent is avoiding combat
ai__combatAttempts = {} -- number of abilities attempted during COMBAT state; if this becomes equal to ai__combatDuration__r, COMBAT ends
ai__debugTable = {} -- debug only
ai__stateTimer = {} -- periodic timer that runs state priority stack
ai__laneNum = {} -- the lane the agent is currently occupying
ai__baseX = {} -- base x coord (return for regen or cast tele after idle reached)
ai__baseY = {} -- base x coord (return for regen or cast tele after idle reached)
ai__totalNum = 0 -- total initiated agent profiles
ai__followUnit = {} -- unit the agent is currently following
ai__regenPointX = {} -- the location the agent retreats to in order to heal
ai__regenPointY = {} -- the location the agent retreats to in order to heal
ai__controlPointTopX = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapTop; ai__laneNum is the reference to grab the point stored here
ai__controlPointMidX = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapMid; ai__laneNum is the reference to grab the point stored here
ai__controlPointBotX = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapBot; ai__laneNum is the reference to grab the point stored here
ai__controlPointTopY = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapTop; ai__laneNum is the reference to grab the point stored here
ai__controlPointMidY = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapMid; ai__laneNum is the reference to grab the point stored here
ai__controlPointBotY = {} -- the furthest point for an agent profile per lane, mapped with ai__controlPointMapBot; ai__laneNum is the reference to grab the point stored here
ai__objPointX = {} -- x,y of where objective is (gathers all agents to this point when obj is active)
ai__objPointY = {} -- x,y of where objective is (gathers all agents to this point when obj is active)
ai__heroId = {} -- the hero Id; used to map other systems e.g. ability profiles
ai__flightPathX = {} -- the position the agent will go to purchase a flight path to a lane
ai__flightPathY = {} -- the position the agent will go to purchase a flight path to a lane
ai__stateCombatTimer = {} -- combat timer that controls behavior events and cadence
-- TODO: ai__abilityOrder = {}
-- [[ trigger vars ---------------------------------]]
ai__trigger__flee = {} -- reference var for FLEE state trigger
-- [[ ----------------------------------------------]]
ai__STATE__INIT = 0 -- initial state
ai__STATE__TRANSIT = 1 -- agent has been directed to move toward location (NON attack-move)
ai__STATE__SITTER = 2 -- agent has been directed to sit at a base until a minion wave comes
ai__STATE__REGEN = 3 -- agent has been directed to regenerate health
ai__STATE__FLEE = 4 -- agent has been directed to retreat toward base direction a certain number of cells
ai__STATE__ENGAGE = 5 -- agent has been directed to continuously attack an enemy target
ai__STATE__IDLE = 6 -- agent has been directed to remain idle
ai__STATE__TELEPORT = 7 -- agent has been directed to teleport back to base
ai__STATE__POKE = 8 -- agent has been directed to run at a target to cast a spell
ai__STATE__DEAD = 9 -- agent is currently respawning
ai__STATE__PUSH = 10 -- agent is currently pushing a lane
ai__STATE__FLIGHT = 11 -- agent is currently pushing a lane
ai__STATE__COMBAT = 12 -- agent is in combat with a target
ai__STATE__AWAITING__FP = 13 -- agent is idle and will next be sent to flight path
ai__STATE__FP = 14 -- agent is moving to flight path
ai__STATE__OBJ = 15 -- agent is targeting an objective point
ai__STATE__WAVECLEAR = 16 -- agent is pushing and not near enemy heroes, so clear minions
ai__STATE_STRINGS = { -- for debug
[0] = "INIT",
[1] = "TRANSIT",
[2] = "SITTER",
[3] = "REGEN",
[4] = "FLEE",
[5] = "ENGAGE",
[6] = "IDLE",
[7] = "TELEPORT",
[8] = "POKE",
[9] = "DEAD",
[10] = "PUSH",
[11] = "FLIGHT",
[12] = "COMBAT",
[13] = "AWAITING_FP",
[14] = "FP",
[15] = "OBJ",
[16] = "WAVECLEAR"
}
ai__laneOccupationHorde = { -- occupied lanes by agent (number of active agent assigned to each lane)
[0] = 0,
[1] = 0,
[2] = 0
}
ai__laneOccupationAlliance = { -- occupied lanes by agent (number of active agent assigned to each lane)
[0] = 0,
[1] = 0,
[2] = 0
}
end
--[[ game data init functions (vars leveraging gg_ need to be declared after global init else they return 0.0 or nil)]]
function DAI__GrabXY()
ai__controlPointMapTopX = {
[0] = GetRectCenterX(gg_rct_minionsHordeTop),
[1] = GetRectCenterX(gg_rct_laneHordeBase1),
[2] = GetRectCenterX(gg_rct_laneHordeBase4),
[3] = GetRectCenterX(gg_rct_laneAllianceBase4),
[4] = GetRectCenterX(gg_rct_laneAllianceBase1),
[5] = GetRectCenterX(gg_rct_minionsAllianceTop)
}
ai__controlPointMapMidX = {
[0] = GetRectCenterX(gg_rct_minionsHordeMid),
[1] = GetRectCenterX(gg_rct_laneHordeBase2),
[2] = GetRectCenterX(gg_rct_laneHordeBase5),
[3] = GetRectCenterX(gg_rct_laneAllianceBase5),
[4] = GetRectCenterX(gg_rct_laneAllianceBase2),
[5] = GetRectCenterX(gg_rct_minionsAllianceMid)
}
ai__controlPointMapBotX = {
[0] = GetRectCenterX(gg_rct_minionsHordeBot),
[1] = GetRectCenterX(gg_rct_laneHordeBase3),
[2] = GetRectCenterX(gg_rct_laneHordeBase6),
[3] = GetRectCenterX(gg_rct_laneAllianceBase6),
[4] = GetRectCenterX(gg_rct_laneAllianceBase3),
[5] = GetRectCenterX(gg_rct_minionsAllianceBot)
}
ai__controlPointMapTopY = {
[0] = GetRectCenterY(gg_rct_minionsHordeTop),
[1] = GetRectCenterY(gg_rct_laneHordeBase1),
[2] = GetRectCenterY(gg_rct_laneHordeBase4),
[3] = GetRectCenterY(gg_rct_laneAllianceBase4),
[4] = GetRectCenterY(gg_rct_laneAllianceBase1),
[5] = GetRectCenterY(gg_rct_minionsAllianceTop)
}
ai__controlPointMapMidY = {
[0] = GetRectCenterY(gg_rct_minionsHordeMid),
[1] = GetRectCenterY(gg_rct_laneHordeBase2),
[2] = GetRectCenterY(gg_rct_laneHordeBase5),
[3] = GetRectCenterY(gg_rct_laneAllianceBase5),
[4] = GetRectCenterY(gg_rct_laneAllianceBase2),
[5] = GetRectCenterY(gg_rct_minionsAllianceMid)
}
ai__controlPointMapBotY = {
[0] = GetRectCenterY(gg_rct_minionsHordeBot),
[1] = GetRectCenterY(gg_rct_laneHordeBase3),
[2] = GetRectCenterY(gg_rct_laneHordeBase6),
[3] = GetRectCenterY(gg_rct_laneAllianceBase6),
[4] = GetRectCenterY(gg_rct_laneAllianceBase3),
[5] = GetRectCenterY(gg_rct_minionsAllianceBot)
}
end
--[[ ------------------------------------------------------]]
-- @n = player number / slot to fill with agent profile
-- @f = faction to generate hero for | 0 for horde, 1 for alliance
function DAI__GenerateProfile(n, f)
math.randomseed( GetTimeOfDay()*10000000*bj_PI )
local r = math.random(0,ai__neutralOdds__i) -- how often agent should pick neutral hero over faction hero
local loc
local loc2
ai__factionNum[n] = f
ai__combatFatigue[n] = false
udg_aiPlayerIsAgent[n] = true -- GUI global ai identifier
if (f == 0) then -- set spawn point
loc = GetRectCenter(gg_rct_spawnHeroHorde)
loc2 = GetRectCenter(gg_rct_flightMasterHorde1)
elseif (f == 1) then
loc = GetRectCenter(gg_rct_spawnHeroAlliance)
loc2 = GetRectCenter(gg_rct_flightMasterAlliance1)
end
ai__baseX[n] = GetLocationX(loc)
ai__baseY[n] = GetLocationY(loc)
ai__flightPathX[n] = GetLocationX(loc2)
ai__flightPathY[n] = GetLocationY(loc2)
-- if neutral hero odds passed OR original faction table is empty, choose neutral; 0=horde, 1=alliance, 2=neutral
if ((#ai__faction__heroes__neutral > 0 and r == ai__neutralOdds__i) or ((f == 0 and #ai__faction__heroes__horde == 0) or (f == 1 and #ai__faction__heroes__alliance == 0))) then
ai__heroId[n] = DAI__RandomizedHeroId(2) -- grab a neutral heroId based on neutral odds
else
ai__heroId[n] = DAI__RandomizedHeroId(f)
end
udg_playerHero[n] = CreateUnitAtLoc(ConvertedPlayer(n), FourCC(ai__heroId[n]), loc, 270.0) -- simulate real player for existing GUI triggers
LoadHero(udg_playerHero[n],n) -- load the hero triggers, controlled by LoadHero under Hero Abilities
ai__playerNum[n] = n
ai__state[n] = ai__STATE__INIT
ai__totalNum = ai__totalNum + 1
ai__debugTable[ai__totalNum] = n -- this won't match player slot index; used only for debug loops
DAI__LaneSortGenerate(n,f)
ai__stateTimer[n] = TimerStart(NewTimer(n), 0.66, true, function() -- initialize timer to contantly check agent state the rest of the game (lower time = more accuracy but less performance)
DAI__StateEngine(n)
end)
local colorString = ''
if n > 5 then -- alliance
colorString = '|cff2c2ce9'
else -- horde
colorString = '|cffff2c2c'
end
local unitName = GetUnitName(udg_playerHero[n])
SetPlayerName(ConvertedPlayer(n),colorString .. unitName .. ' (|r|cff00e9ffBot|r' .. colorString .. ')|r')
ShowUnit(udg_playerShop[n], true)
ShowUnit(udg_playerShopElite[n], true)
LeaderboardUpdatePlayerName(n, "(" .. n .. ") " .. unitName, nil, true)
LeaderboardUpdateEliminations(n)
LeaderboardUpdateDeaths(n)
LeaderboardUpdateXP(n)
RemoveLocation(loc)
RemoveLocation(loc2)
-- set up tower flee listener
ai__trigger__flee[n] = CreateTrigger()
local func = function()
local pNumb = GetConvertedPlayerId(GetOwningPlayer(udg_DamageEventTarget))
ai__state[pNumb] = ai__STATE__FLEE
DAI__MoveToControlPoint(pNumb, "move")
TimerStart(NewTimer(),ai__combatFleeDuration__r,true,function()
if (not DAI__StateIsNearHostileTowers(pNumb,600.0)) then
ai__state[pNumb] = ai__STATE__IDLE
ReleaseTimer()
else
DAI__MoveToControlPoint(pNumb, "move")
end
end)
end
TriggerRegisterVariableEvent( ai__trigger__flee[n], "udg_DamageEvent", EQUAL, 1.00 )
TriggerAddCondition( ai__trigger__flee[n], Filter( function() return udg_DamageEventTarget == udg_playerHero[n]
and ( IsUnitType(udg_DamageEventSource,UNIT_TYPE_STRUCTURE) or IsUnitType(udg_DamageEventSource,UNIT_TYPE_ANCIENT) )
and udg_DamageEventAttackT ~= udg_ATTACK_TYPE_HERO
and ai__state[n] ~= ai__STATE__FLEE
end ) )
TriggerAddAction(ai__trigger__flee[n],func)
end
-- @n = player number (int) under agent control
-- @f = faction number (0 = horde, 1 = alliance)
function DAI__LaneSortGenerate(n,f)
--DebugMsg( "attempting lane init for: " .. n )
local laneCap = 0
local x
local y
if (f == 0) then
ai__controlPointTopX[n] = ai__controlPointMapTopX[2]
ai__controlPointMidX[n] = ai__controlPointMapMidX[2]
ai__controlPointBotX[n] = ai__controlPointMapBotX[2]
ai__controlPointTopY[n] = ai__controlPointMapTopY[2]
ai__controlPointMidY[n] = ai__controlPointMapMidY[2]
ai__controlPointBotY[n] = ai__controlPointMapBotY[2]
for lane = 0,2,1 do
if (ai__laneOccupationHorde[lane] < ai__laneConfig__t[lane]) then
ai__laneOccupationHorde[lane] = ai__laneOccupationHorde[lane] + 1
ai__laneNum[n] = lane -- assign lane
DAI__UpdateRegenPointToCP(n) -- update where the agent moves to in order to restore health
--DebugMsg(ai__laneNum[n] .. " is assigned lane for " .. n)
break
end
end
elseif (f == 1) then
ai__controlPointTopX[n] = ai__controlPointMapTopX[3]
ai__controlPointMidX[n] = ai__controlPointMapMidX[3]
ai__controlPointBotX[n] = ai__controlPointMapBotX[3]
ai__controlPointTopY[n] = ai__controlPointMapTopY[3]
ai__controlPointMidY[n] = ai__controlPointMapMidY[3]
ai__controlPointBotY[n] = ai__controlPointMapBotY[3]
for lane = 0,2,1 do
if (ai__laneOccupationAlliance[lane] < ai__laneConfig__t[lane]) then
ai__laneOccupationAlliance[lane] = ai__laneOccupationAlliance[lane] + 1
ai__laneNum[n] = lane -- assign lane
DAI__UpdateRegenPointToCP(n) -- update where the agent moves to in order to restore health
break
end
end
end
end
-- runs a series of functions that prioritize what the agent should be doing; this function controls the agent movement based on state returns
-- @n = player number to run state engine for
function DAI__StateEngine(n)
--[[ AI NON-COMBAT STATE MANAGEMENT ]]
if (IsUnitAliveBJ(udg_playerHero[n])) then
if (ai__state[n] == ai__STATE__FP) then -- command agent to move toward and take FP to lane
if (IsUnitInRangeXY( udg_playerHero[n], ai__flightPathX[n], ai__flightPathY[n], 320.0 ) ) then -- fake a FP purchase
DAI__PurchaseFlightPath(n)
ai__state[n] = ai__STATE__IDLE
elseif (not IsUnitInRangeXY( udg_playerHero[n], ai__flightPathX[n], ai__flightPathY[n], 1750.0 )) then -- [debugger] if agent entered FP state outside of FP bounds, reset
ai__state[n] = ai__STATE__IDLE
DAI__MoveToControlPoint(n, "attack")
else -- move toward
DAI__MoveToFlightPoint(n, "move")
end
elseif (ai__state[n] == ai__STATE__AWAITING__FP and not DAI__StateHealthIsLow(n)) then -- if was commanded to holdposition but is now healthy, continue
ai__state[n] = ai__STATE__FP
else
if (ai__state[n] ~= ai__STATE__COMBAT and ai__state[n] ~= ai__STATE__WAVECLEAR) then -- if objective is not in progress and not in combat, run lane behavior
if (ai__state[n] == ai__STATE__TELEPORT) then
if (DAI__StateIsSafe(n) or DAI__StateIsSafeToPush(n,0)) then -- if safe, do nothing and continue to teleport unless already at base (distance check)
if (DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__baseX[n],ai__baseY[n]) <= 500.0 and GetUnitLifePercent(udg_playerHero[n]) > ai__safeHealth__r) then -- if agent is healthy, reset to idle
ai__state[n] = ai__STATE__IDLE
end
else -- if dangerous, cancel teleport and manually transit to regen point
DAI__MoveToRegenPoint(n)
if (not DAI__StateHealthIsLow(n)) then
ai__state[n] = ai__STATE__IDLE
end
end
else
if (DAI__StateIsAtBase(n) and GetUnitLifePercent(udg_playerHero[n]) < ai__safeHealth__r and ai__state[n] ~= ai__STATE__AWAITING__FP) then
IssueImmediateOrder(udg_playerHero[n], "holdposition")
ai__state[n] = ai__STATE__AWAITING__FP -- prepare for FP
elseif (DAI__StateIsAtBase(n) and GetUnitLifePercent(udg_playerHero[n]) > ai__safeHealth__r) then
DAI__MoveToControlPoint(n, "attack")
ai__state[n] = ai__STATE__FP -- command ai to go purchase a flight path to their lane
elseif (DAI__StateHealthIsLow(n) and ai__state[n] ~= ai__STATE__REGEN) then -- retreat if health is low
DAI__MoveToRegenPoint(n)
ai__state[n] = ai__STATE__REGEN
elseif (ai__state[n] == ai__STATE__REGEN) then -- wait for regen to complete, then make idle which will allow DAI__MoveToControlPoint to operate
if (DAI__StateIsSafe(n) and not DAI__StateIsNearHostileTowers(n,0)
and DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__regenPointX[n],ai__regenPointY[n]) > ai__teleportRegenRange__r and not DAI__StateIsAtRegenPoint(n)) then -- teleport home if not near regen point
DAI__TeleportHome(n)
ai__state[n] = ai__STATE__TELEPORT
TimerStart(NewTimer(n), 8.0, false, function() -- safety net if critical state error occurs
if (ai__state[n] == ai__STATE__TELEPORT) then ai__state[n] = ai__STATE__IDLE end
ReleaseTimer()
end)
elseif (not DAI__StateHealthIsLow(n, ai__lowHealth__r + 12.0) and DAI__StateIsSafeToPush(n,0)) then -- listen for a health restore to re-engage (e.g. a healer)
ai__state[n] = ai__STATE__PUSH
DAI__FollowNearbyMinion(n,0)
elseif (not DAI__StateHealthIsLow(n, ai__lowHealth__r + 12.0) and DAI__StateIsNearEnemyHero(n)) then -- listen for a health restore to re-engage (e.g. a healer)
ai__state[n] = ai__STATE__COMBAT
ai__combatAttempts[n] = 0
elseif (not DAI__StateIsAtRegenPoint(n)) then
DAI__MoveToRegenPoint(n)
end
if (GetUnitLifePercent(udg_playerHero[n]) > ai__safeHealth__r) then
if (DAI__StateIsSafeToPush(n,2500.0)) then
DAI__FollowNearbyMinion(n,2500.0)
ai__state[n] = ai__STATE__PUSH
else
ai__state[n] = ai__STATE__IDLE
end
--DebugMsg("state engine: " .. n .. "; health restored, return to idle")
elseif (not DAI__StateIsSafe(n) and DAI__StateIsAtRegenPoint(n)) then
IssueImmediateOrder(udg_playerHero[n], "holdposition")
--DebugMsg("state engine: " .. n .. "; threat detected nearby while REGEN, hold position")
end
elseif (udg_aiTargetObjective and not ai__ignoreObj[n] and ai__state[n] ~= ai__STATE__OBJ) then -- obj targeting in progress
ai__state[n] = ai__STATE__OBJ -- toggle objective movement when obj active
DAI__MoveToObjectivePoint(n, "attack")
elseif (ai__state[n] == ai__STATE__OBJ and udg_aiTargetObjective == false) then -- obj targeting has ended
DAI__MoveToRegenPoint(n)
ai__state[n] = ai__STATE__REGEN -- if objective is over, send back to base to heal and resume lane pushing
elseif (ai__state[n] == ai__STATE__OBJ) then -- do objective behavior
if (DAI__StateIsNearEnemyHero(n) and not DAI__StateIsNearHostileTowers(n,500.0)) then
if (not ai__combatFatigue[n]) then -- if agent has not engaged recently, engage
ai__state[n] = ai__STATE__COMBAT
ai__combatAttempts[n] = 0
end
else
DAI__MoveToObjectivePoint(n, "move")
end
elseif (not DAI__StateIsSafeToPush(n,0) and ai__state[n] == ai__STATE__PUSH) then -- if push in progress and no longer safe, begin retreat
DAI__MoveToControlPoint(n, "move")
ai__state[n] = ai__STATE__TRANSIT
--DebugMsg("state engine: " .. n .. "; push is no longer safe, running DAI__MoveToControlPoint")
elseif (DAI__StateIsSafeToPush(n,0) and DAI__StateIsNearEnemyMinionCount(n, 0, 3) and ai__state[n] == ai__STATE__PUSH
and not DAI__StateIsNearEnemyHero(n)) then -- if pushing and not near enemy hero, and near x minimum minions, engage minions
if (not ai__combatFatigue[n]) then -- if agent has not engaged recently, engage
ai__state[n] = ai__STATE__WAVECLEAR
ai__combatAttempts[n] = 0
end
elseif ((DAI__StateIsSafeToPush(n,0) and ai__state[n] ~= ai__STATE__PUSH)) then -- initiate push down lane
DAI__FollowNearbyMinion(n,0)
ai__state[n] = ai__STATE__PUSH
--DebugMsg("state engine: " .. n .. "; minion wave found, follow")
elseif ai__state[n] == ai__STATE__PUSH and ai__followUnit[n] ~= nil and not IsUnitAliveBJ(ai__followUnit[n]) then -- is lane push follow unit dead?
ai__followUnit[n] = nil
if (DAI__StateIsSafeToPush(n,0)) then
DAI__FollowNearbyMinion(n,0)
else
DAI__MoveToControlPoint(n, "attack")
ai__state[n] = ai__STATE__TRANSIT
end
elseif (ai__state[n] == ai__STATE__PUSH
and ai__followUnit[n] ~= nil
and IsUnitAliveBJ(ai__followUnit[n])
and DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),GetUnitX(ai__followUnit[n]),GetUnitY(ai__followUnit[n])) > ai__followWaveLeashRange__r ) then -- is lane push follow unit dead?
IssueTargetOrder(udg_playerHero[n],"smart",ai__followUnit[n])
elseif (DAI__StateIsNearEnemyHero(n) and ai__state[n] == ai__STATE__PUSH and not DAI__StateIsNearHostileTowers(n,550.0)) then
if (not ai__combatFatigue[n]) then -- if agent has not engaged recently, engage
ai__state[n] = ai__STATE__COMBAT
ai__combatAttempts[n] = 0
end
elseif (ai__state[n] == ai__STATE__IDLE and not DAI__StateIsAtControlPoint(n)) then -- if caught idle somewhere else on map, go to CP
DAI__MoveToControlPoint(n, "move")
ai__state[n] = ai__STATE__TRANSIT
elseif (ai__state[n] == ai__STATE__TRANSIT and not DAI__StateIsAtControlPoint(n)) then -- if CP transit in progress, keep moving
DAI__MoveToControlPoint(n, "move")
elseif (ai__state[n] == ai__STATE__TRANSIT and DAI__StateIsAtControlPoint(n)) then -- if sitting
ai__state[n] = ai__STATE__SITTER
elseif (not DAI__StateIsSafeToPush(n,0) and (DAI__StateIsAtControlPoint(n) or DAI__StateIsAtRegenPoint(n))) then
ai__state[n] = ai__STATE__IDLE
end
end -- end tele check block
else -- if state is combat, do:
--[[ AI COMBAT STATE MANAGEMENT ]]
if ((ai__stateCombatTimer[n] == nil or TimerGetRemaining(ai__stateCombatTimer[n]) == 0.0) and ai__combatAttempts[n] == 0 and (ai__state[n] == ai__STATE__COMBAT or ai__state[n] == ai__STATE__WAVECLEAR)) then -- directly engage
ai__combatAttempts[n] = 1
ai__combatStartingHealthPerc[n] = GetUnitLifePercent(udg_playerHero[n])
local isTargetHero -- true = target hero; false = minion
if (ai__state[n] == ai__STATE__WAVECLEAR) then isTargetHero = false else isTargetHero = true end
ai__stateCombatTimer[n] = NewTimer()
TimerStart(ai__stateCombatTimer[n], ai__combatAbilityCadence__r, true, function()
ai__combatAttempts[n] = ai__combatAttempts[n] + 1
if (ai__combatAttempts[n] > ai__combatAttemptsAllowed
or DAI__StateHealthIsLow(n)
or not DAI__StateIsSafeToPush(n,0)
or DAI__StateIsNearHostileTowers(n,650.0))
or (ai__combatStartingHealthPerc[n] - GetUnitLifePercent(udg_playerHero[n]) >= ai__combatDisengageHealthPerc__r
or (ai__state[n] ~= ai__STATE__COMBAT
and ai__state[n] ~= ai__STATE__WAVECLEAR) )
then -- if agent is in danger or combat window has ended, retreat
-- if combat ending conditions raised, end combat
DAI__MoveToControlPoint(n, "move")
ai__combatFatigue[n] = true -- prevent further COMBAT states
TimerStart(NewTimer(), ai__combatFatigueDuration__r, false, function()
ai__combatFatigue[n] = false -- after engagement ends, allow COMBAT again
if (DAI__StateHealthIsLow(n)) then
DAI__MoveToRegenPoint(n, "move")
ai__state[n] = ai__STATE__IDLE
else
DAI__MoveToControlPoint(n, "move")
ai__state[n] = ai__STATE__IDLE
end
ReleaseTimer()
end)
ReleaseTimer()
ai__stateCombatTimer[n] = nil
else
-- if combat ending conditions passed, initiate combat
DAI__InitiateCombatBasic(n, isTargetHero)
end
end)
else
-- TODO: alternative state to command agent to dodge abilities, etc.
-- This can be a listener instead of a state timer event
-- --::: e.g. create a trigger for when damaged by hero or a hostile
-- --::: spell is used at a point nearby, run checks on surroundings,
-- --::: command agent to shuffle around or dodge, etc.
-- --::: can use doltish profiles' ability type (point unit target etc.)
-- -- :::
end
--[[----------------------------]]
end -- end combat check block
end -- end FP check block
else
ai__state[n] = ai__STATE__DEAD
end -- end check if alive block
end
-- @x,y = location for Horde focus point
-- @x2,y2 = location for Alliance focus point
function DAI__StateObjectiveIsActive(x,y,x2,y2)
-- set objective ability behavior
ai__combatFatigueDuration__r = ai__combatFatigueDurationObj__r
ai__combatAbilityCadence__r = ai__combatAbilityCadenceObj__r
-- explicitly set agents to attack or defend for now until we get smarter later:
for n = 1,5 do -- horde
if (fp__factionMapHorde[2] >= ai__objCenterBasesReq__i) then -- only send if agents can safely get there, until we get smarter
if (n == 1 or n == 2 or n == 3 or n == 4) then -- horde, send agents to attack
ai__objPointX[n] = x
ai__objPointY[n] = y
elseif (n == 5) then -- horde, send agents to defend
ai__objPointX[n] = x2
ai__objPointY[n] = y2
end
ai__ignoreObj[n] = false
ai__state[n] = ai__STATE__OBJ
else
ai__ignoreObj[n] = true
end
end
for n = 6,10 do --alliance
if (fp__factionMapAlliance[2] <= (5 - ai__objCenterBasesReq__i)) then -- only send if agents can safely get there, until we get smarter
if (n == 6 or n == 7 or n == 8 or n == 9) then -- alliance, send agents to attack
ai__objPointX[n] = x2
ai__objPointY[n] = y2
elseif (n == 10) then -- alliance, send agents to defend
ai__objPointX[n] = x
ai__objPointY[n] = y
end
ai__ignoreObj[n] = false
ai__state[n] = ai__STATE__OBJ
else
ai__ignoreObj[n] = true
end
end
end
-- :: return to CP after objective ends
function DAI__StateObjectiveIsInactive()
-- reset objective ability to lane behavior
ai__combatFatigueDuration__r = ai__combatFatigueDurationD__r
ai__combatAbilityCadence__r = ai__combatAbilityCadenceD__r
for n = 1,10 do
ai__state[n] = ai__STATE__REGEN
end
end
-- @n = player number to initiate agent move
function DAI__MoveToObjectivePoint(n, s)
DAI__Move(n,s,ai__objPointX[n],ai__objPointY[n])
end
-- sets the agent's point of return to restore health based on control point
-- @n = agent to update
function DAI__UpdateRegenPointToCP(n)
ai__regenPointX[n] = ai__baseX[n]
ai__regenPointY[n] = ai__baseY[n]
end
-- is the agent sitting and their assigned point?
-- @n = player number to run state check for
function DAI__StateIsAtControlPoint(n)
local bool = false
local distance
if (ai__laneNum[n] == 0) then
distance = DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__controlPointTopX[n],ai__controlPointTopY[n])
elseif (ai__laneNum[n] == 1) then
distance = DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__controlPointMidX[n],ai__controlPointMidY[n])
elseif (ai__laneNum[n] == 2) then
distance = DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__controlPointBotX[n],ai__controlPointBotY[n])
end
if (distance <= 350.0) then
bool = true
end
return bool
end
-- is the agent sitting and their assigned point?
-- @n = player number to run state check for
function DAI__StateIsAtRegenPoint(n)
local bool = false
if (DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__regenPointX[n],ai__regenPointY[n]) <= 299.0) then
bool = true
end
return bool
end
-- is the agent sitting at base?
-- @n = player number to run state check for
function DAI__StateIsAtBase(n)
bool = false
if (DistanceBetweenXY(GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]),ai__baseX[n],ai__baseY[n]) <= 450.0) then
bool = true
end
return bool
end
-- find out if there are nearby allies to encourage agent to push forward
-- @n = player number to run state check for; returns true if greater than threshold
-- @range = optional range to look for
function DAI__StateIsSafeToPush(n, range)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
local r = ai__isSafeToPushRange__r
if (range > 0) then
r = range
end
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitAlly(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL)) and GetConvertedPlayerId(GetOwningPlayer(GetFilterUnit())) >= 13 end ) )
size = BlzGroupGetSize(g)
if (size >= ai__feelsSafeThreshold__i) then
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby hostile structures
-- @n = player number to run state check for; returns true if greater than threshold
-- @range = optional, overrides default range check (ai__isNearTowersRange__r)
function DAI__StateIsNearHostileTowers(n, range)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
local r = ai__isNearTowersRange__r
if (range ~= nil and range > 0.0) then
r = range
end
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)) end ) )
size = BlzGroupGetSize(g)
if (size > 0) then
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby enemy heroes
-- @n = player number to run state check for; returns true if greater than threshold
function DAI__StateIsNearEnemyHero(n)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
GroupEnumUnitsInRange(g, x, y, ai__isNearEnemyHeroRange__r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
size = BlzGroupGetSize(g)
if (size > 0) then
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby enemy minions
-- @n = player number to run state check for; returns true if greater than threshold
function DAI__StateIsNearEnemyMinionCount(n, range, count)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
local r = ai__isNearEnemyMinionRange__r
local c = ai__isNearEnemyMinionCount__i
if (count ~= nil and count > 0) then
c = count
end
if (range ~= nil and range > 0.0) then
r = range
end
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
size = BlzGroupGetSize(g)
if (size > c) then
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby ally heroes
-- @n = player number to run state check for; returns true if greater than threshold
function DAI__StateIsNearAllyHero(n)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
GroupEnumUnitsInRange(g, x, y, ai__isNearAllyHeroRange__r, Condition( function() return IsUnitAlly(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
size = BlzGroupGetSize(g)
if (size > 0) then
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby allies to encourage agent to push forward
-- @n = player number to run state check for; returns true if greater than threshold
-- @range = optional range to search for
function DAI__FollowNearbyMinion(n, range)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = false
local r = ai__isSafeToPushRange__r
if (range ~= nil and range > 0.0) then
r = range
end
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return IsUnitAlly(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_SUMMONED)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL)) and GetConvertedPlayerId(GetOwningPlayer(GetFilterUnit())) >= 13 end ) )
size = BlzGroupGetSize(g)
if (size > 0) then
ai__followUnit[n] = GroupPickRandomUnit(g)
IssueTargetOrder(udg_playerHero[n],"smart",ai__followUnit[n])
bool = true
end
DestroyGroup(g)
return bool
end
-- find out if there are nearby enemies
-- @n = player number to run state check for; returns true if 0 nearby enemies
function DAI__StateIsSafe(n)
local g = CreateGroup()
local x = GetUnitX(udg_playerHero[n])
local y = GetUnitY(udg_playerHero[n])
local size = 0
local p = ConvertedPlayer(n)
local bool = true
GroupEnumUnitsInRange(g, x, y, ai__isSafeRange__r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)) end ) )
size = BlzGroupGetSize(g)
if (size > 0) then
bool = false
end
DestroyGroup(g)
return bool
end
-- @n = player number to tele home for
function DAI__TeleportHome(n)
IssuePointOrder(udg_playerHero[n],"massteleport",ai__baseX[n],ai__baseY[n])
end
-- @i = agent player num
-- @l = lane num (0 = top, 1 = mid, 2 = bot)
-- returns x, y
function DAI__GetControlPointXY(n, l)
local x
local y
if (l == 0) then
x = ai__controlPointTopX[n]
y = ai__controlPointTopY[n]
elseif (l == 1) then
x = ai__controlPointMidX[n]
y = ai__controlPointMidY[n]
elseif (l == 2) then
x = ai__controlPointBotX[n]
y = ai__controlPointBotY[n]
else
return false
end
return x, y
end
-- @n = player number to initiate agent move
function DAI__MoveToControlPoint(n, s)
local x
local y
local l = ai__laneNum[n]
x,y = DAI__GetControlPointXY(n, l)
DAI__Move(n,s,x,y)
end
-- @n = player number to initiate agent move
function DAI__MoveToFlightPoint(n, s)
IssuePointOrder(udg_playerHero[n], s, ai__flightPathX[n], ai__flightPathY[n])
end
-- @n = player number to initiate agent move
function DAI__MoveToRegenPoint(n)
local x = ai__regenPointX[n]
local y = ai__regenPointY[n]
DAI__Move(n,"move",x,y)
end
-- since not possible to command to purchase, "fake" a purchase
-- @n = player number for agent to buy FP
function DAI__PurchaseFlightPath(n)
local itemId
if (udg_aiTargetObjective == true) then
itemId = FourCC('I004')
else
if (ai__laneNum[n] == 0) then -- top
itemId = FourCC('I002')
elseif (ai__laneNum[n] == 1) then -- mid
itemId = FourCC('I004')
elseif (ai__laneNum[n] == 2) then -- bot
itemId = FourCC('I003')
end
end
UnitAddItemById(udg_playerHero[n],itemId)
end
-- begin auto attack a target
-- @n = player number to command
-- @unit = unit to attack
function DAI__AttackTarget(n, unit)
IssueTargetOrder(udg_playerHero[n],"attack",unit)
end
-- @a unit = unit to direct
-- @n int = player number (int) under agent control
-- @x int = x coord to move to
-- @y int = y coord to move to
-- @s string = movement type to initiate
function DAI__Move(n,s,x,y)
IssuePointOrder(udg_playerHero[n], s, x, y)
end
-- updates active agent control points when base structures die
-- flightPath points are map-specific points updated to successive bases; we lazily reference them
function DAI__UpdateControlPoints()
for n = 1,10,1 do
if (ai__regenPointX[n] ~= ai__baseX[n] and ai__regenPointY[n] ~= ai__baseY[n]) then -- don't update if regen point has been set to home for assigned lane
if (ai__factionNum[n] == 0) then
ai__controlPointTopX[n] = GetLocationX(udg_flightPathFurthestHorde[1])
ai__controlPointTopY[n] = GetLocationY(udg_flightPathFurthestHorde[1])
ai__controlPointMidX[n] = GetLocationX(udg_flightPathFurthestHorde[2])
ai__controlPointMidY[n] = GetLocationY(udg_flightPathFurthestHorde[2])
ai__controlPointBotX[n] = GetLocationX(udg_flightPathFurthestHorde[3])
ai__controlPointBotY[n] = GetLocationY(udg_flightPathFurthestHorde[3])
elseif (ai__factionNum[n] == 1) then
ai__controlPointTopX[n] = GetLocationX(udg_flightPathFurthestAlliance[1])
ai__controlPointTopY[n] = GetLocationY(udg_flightPathFurthestAlliance[1])
ai__controlPointMidX[n] = GetLocationX(udg_flightPathFurthestAlliance[2])
ai__controlPointMidY[n] = GetLocationY(udg_flightPathFurthestAlliance[2])
ai__controlPointBotX[n] = GetLocationX(udg_flightPathFurthestAlliance[3])
ai__controlPointBotY[n] = GetLocationY(udg_flightPathFurthestAlliance[3])
end
DAI__UpdateRegenPointToCP(n)
else -- if regen point is home, set control point to new minion spawn points (near main core)
if (ai__factionNum[n] == 0) then
ai__controlPointTopX[n] = GetLocationX(udg_spawnPointHorde[0])
ai__controlPointTopY[n] = GetLocationY(udg_spawnPointHorde[0])
ai__controlPointMidX[n] = GetLocationX(udg_spawnPointHorde[1])
ai__controlPointMidY[n] = GetLocationY(udg_spawnPointHorde[1])
ai__controlPointBotX[n] = GetLocationX(udg_spawnPointHorde[2])
ai__controlPointBotY[n] = GetLocationY(udg_spawnPointHorde[2])
elseif (ai__factionNum[n] == 1) then
ai__controlPointTopX[n] = GetLocationX(udg_spawnPointAlliance[0])
ai__controlPointTopY[n] = GetLocationY(udg_spawnPointAlliance[0])
ai__controlPointMidX[n] = GetLocationX(udg_spawnPointAlliance[1])
ai__controlPointMidY[n] = GetLocationY(udg_spawnPointAlliance[1])
ai__controlPointBotX[n] = GetLocationX(udg_spawnPointAlliance[2])
ai__controlPointBotY[n] = GetLocationY(udg_spawnPointAlliance[2])
end
end
end
end
-- if all bases in a lane are dead, sets agent control point to home base (to return when low health etc)
-- @l = lane to run control point check for
function DAI__SetControlPointHome(l, f)
for n = 1,10,1 do
if (ai__laneNum[n] == l and ai__factionNum[n] == f) then -- pass the faction of the lane to update
ai__regenPointX[n] = ai__baseX[n]
ai__regenPointY[n] = ai__baseY[n]
if (l == 0 and ai__factionNum[n] == 0) then
ai__controlPointTopX[n] = GetLocationX(udg_spawnPointHorde[0])
ai__controlPointTopY[n] = GetLocationY(udg_spawnPointHorde[0])
elseif (l == 1 and ai__factionNum[n] == 0) then
ai__controlPointMidX[n] = GetLocationX(udg_spawnPointHorde[1])
ai__controlPointMidY[n] = GetLocationY(udg_spawnPointHorde[1])
elseif (l == 2 and ai__factionNum[n] == 0) then
ai__controlPointBotX[n] = GetLocationX(udg_spawnPointHorde[2])
ai__controlPointBotY[n] = GetLocationY(udg_spawnPointHorde[2])
elseif (l == 0 and ai__factionNum[n] == 1) then
ai__controlPointTopX[n] = GetLocationX(udg_spawnPointAlliance[0])
ai__controlPointTopY[n] = GetLocationY(udg_spawnPointAlliance[0])
elseif (l == 1 and ai__factionNum[n] == 1) then
ai__controlPointMidX[n] = GetLocationX(udg_spawnPointAlliance[1])
ai__controlPointMidY[n] = GetLocationY(udg_spawnPointAlliance[1])
elseif (l == 2 and ai__factionNum[n] == 1) then
ai__controlPointBotX[n] = GetLocationX(udg_spawnPointAlliance[2])
ai__controlPointBotY[n] = GetLocationY(udg_spawnPointAlliance[2])
end
end
end
end
-- @f = faction int to run randomizer for, returns heroId for a hero from that faction
function DAI__RandomizedHeroId(f)
local heroId
local i = 0
local r = 0
if (f == 0) then
r = math.random(1, #ai__faction__heroes__horde)
repeat
heroId = ai__faction__heroes__horde[r]
i=i+1
until(heroId ~= nil or i == 124)
elseif (f == 1) then
r = math.random(1, #ai__faction__heroes__alliance)
repeat
heroId = ai__faction__heroes__alliance[r]
i=i+1
until(heroId ~= nil or i == 124)
elseif (f == 2) then
r = math.random(1, #ai__faction__heroes__neutral)
repeat
heroId = ai__faction__heroes__neutral[r]
i=i+1
until(heroId ~= nil or i == 124)
else
return false
end
--DAI__RemoveHeroId(heroId, f)
--DebugMsg( "hero id randomized for faction " .. f .. " is: " .. heroId )
return heroId
end
-- @heroId = heroId to compare against all other players
function DAI__IsHeroUnique(heroId)
for i = 1,10,1 do
if (heroId == udg_ZplayerHeroId[i]) then
return false
end
end
return true
end
-- @heroId = hero Id string to remove from possible heroes array
-- @f = faction to remove hero from (0 = horde, 1 = alliance, 2 = neutral)
function DAI__RemoveHeroId(heroId, f)
local int = 0
if (f == 0) then
table.remove(ai__faction__heroes__horde,GetTableIndexByValue(ai__faction__heroes__horde,heroId))
elseif (f == 1) then
table.remove(ai__faction__heroes__alliance,GetTableIndexByValue(ai__faction__heroes__alliance,heroId))
elseif (f == 2) then
table.remove(ai__faction__heroes__neutral,GetTableIndexByValue(ai__faction__heroes__neutral,heroId))
else
return false
end
--DebugMsg( "hero id removed: " .. heroId )
--int = #ai__faction__heroes__horde
--DebugMsg( "horde size is now: " .. int )
--int = #ai__faction__heroes__alliance
--DebugMsg( "alliance size is now: " .. int )
--int = #ai__faction__heroes__neutral
--DebugMsg( "neutral size is now: " .. int )
end
-- @n = player number (int) under agent control
-- @amount = optional amount to check, returns if under this amount (e.g. 30 = 30%)
function DAI__StateHealthIsLow(n, amountPercent)
local a = ai__lowHealth__r
if (amountPercent ~= nil and amountPercent ~= 0.0) then
a = amountPercent
end
if (GetUnitLifePercent(udg_playerHero[n]) <= a) then
--DebugMsg("set state for " .. n .. " to REGEN")
return true
else
return false
end
end
function PrintMe()
local i = 0
local int
for i = 1,10,1 do
int = tostring(ai__STATE_STRINGS[ai__state[i]])
DebugMsg("state for " .. tostring(i) .. " is: " .. int)
end
end
-- run this to generate agent for absent players
function GenerateAI()
local d = 0
local f = 0
--[[ run init functions (have to declare after global init)]]
DAI__GrabXY()
--[[ ------------------------------------------------------]]
TimerStart(NewTimer(d, f), .50, true, function() -- run a timer to properly set lua randomseed based on game time and to prevent stack bugs when framerate sticks
d = d + 1
if (d == 6) then
f = 1 -- increment faction
end
if (udg_gamePlayerEmptyComp[d] == true) then -- to prevent desync, we check in map init for empty slots or comps
DAI__GenerateProfile(d,f)
end
if (d >= 10)then
ReleaseTimer()
--DebugMsg("generate exit")
end
end)
end
-- generate a single agent manually
-- @n = player
-- @heroId = optional hero id to general specific hero (NOT IMPLEMENTED YET)
function GenerateSingleAI(n, heroId)
DAI__GrabXY()
local f = 0
if (n >= 6) then
f = 1
end
DAI__GenerateProfile(n,f)
end
-- in order for -ai command to be asynchronous with player selection (for now), we generate hero sheet regardless
ai__faction__heroes__horde = {}
ai__faction__heroes__alliance = {}
ai__faction__heroes__neutral = {}
-- insert possible hero ids for HORDE -- NOT added due to mechanics conflict: acolyte
ai__faction__heroes__horde[#ai__faction__heroes__horde+1] = "O00C" -- grunt
ai__faction__heroes__horde[#ai__faction__heroes__horde+1] = "O010" -- shaman
ai__faction__heroes__horde[#ai__faction__heroes__horde+1] = "O00Q" -- tauren
ai__faction__heroes__horde[#ai__faction__heroes__horde+1] = "O00R" -- crypt fiend
ai__faction__heroes__horde[#ai__faction__heroes__horde+1] = "O00H" -- gargoyle
-- insert possible hero ids for ALLIANCE -- NOT added due to mechanics conflict: wisp
ai__faction__heroes__alliance[#ai__faction__heroes__alliance+1] = "O00E" -- footman
ai__faction__heroes__alliance[#ai__faction__heroes__alliance+1] = "O012" -- knight
ai__faction__heroes__alliance[#ai__faction__heroes__alliance+1] = "O00T" -- spell breaker
ai__faction__heroes__alliance[#ai__faction__heroes__alliance+1] = "E000" -- huntress
ai__faction__heroes__alliance[#ai__faction__heroes__alliance+1] = "O00M" -- priest
-- insert possible hero ids for NEUTRAL -- NOT added due to mechanics conflict: n/a
ai__faction__heroes__neutral[#ai__faction__heroes__neutral+1] = "O00J" -- eredar warlock
ai__faction__heroes__neutral[#ai__faction__heroes__neutral+1] = "O00S" -- fel orc warlock
ai__faction__heroes__neutral[#ai__faction__heroes__neutral+1] = "O013" -- hydra
ai__faction__heroes__neutral[#ai__faction__heroes__neutral+1] = "O014" -- tuskarr healer
ai__faction__heroes__neutral[#ai__faction__heroes__neutral+1] = "O016" -- overlord
function DAI__VarGenProfiles()
-- matches order string with hero ability
-- Q,W,E,R,F
ai__profileAbilityOrderString = {
["O00C"] = {"slow","roar","taunt","fingerofdeath","battleroar"}, -- grunt
["O010"] = {"slow","lightningshield","fingerofdeath","bloodlust","ward"}, -- shaman
["O00Q"] = {"rainoffire","roar","fingerofdeath","taunt",""}, -- tauren
["O00R"] = {"rejuvination","fingerofdeath","antimagicshell","clusterrockets","unholyfrenzy"}, -- crypt fiend
["O00H"] = {"rainoffire","unholyfrenzy","metamorphosis","ward",""}, -- gargoyle
["O00E"] = {"slow","holybolt","thunderbolt","ward",""}, -- footman
["O012"] = {"slow","immolation","mirrorimage","rainoffire",""}, -- knight
["O00T"] = {"fingerofdeath","taunt","roar","howlofterror",""}, -- spell breaker
["E000"] = {"ensnare","ward","ambush","rainoffire",""}, -- huntress
["O00M"] = {"heal","curse","holybolt","metamorphosis","roar"}, -- priest
["O00J"] = {"clusterrockets","rainoffire","ward","darksummoning","unholyfrenzy"}, -- eredar warlock
["O00S"] = {"deathcoil",--[["immolation"]]"","roar","unholyfrenzy",""}, -- fel orc warlock
["O013"] = {"carrionswarm","breathoffrost","breathoffire","fingerofdeath",""}, -- hydra
["O014"] = {"rainoffire","clusterrockets","ward","fingerofdeath","roar"}, -- tuskarr healer
["O016"] = {"rainoffire","spiritlink","antimagicshell","howlofterror",""} -- tuskarr healer
}
-- controls the order by which abilities should be used; lower value = use first
ai__profileAbilityOrderPriority = {
["O00C"] = {1,2,3,4,5}, -- grunt
["O010"] = {1,4,2,5,3}, -- shaman
["O00Q"] = {1,4,2,3,5}, -- tauren
["O00R"] = {1,2,3,4,5}, -- crypt fiend
["O00H"] = {1,2,3,4,5}, -- gargoyle
["O00E"] = {2,4,1,3,5}, -- footman
["O012"] = {1,2,3,4,5}, -- knight
["O00T"] = {1,3,2,4,5}, -- spell breaker
["E000"] = {1,2,3,4,5}, -- huntress
["O00M"] = {1,2,3,4,5}, -- priest
["O00J"] = {1,2,3,4,5}, -- eredar warlock
["O00S"] = {2,1,4,3,5}, -- fel orc warlock
["O013"] = {1,2,3,4,5}, -- hydra
["O014"] = {1,3,2,4,5}, -- tuskarr healer
["O016"] = {2,3,1,4,5} -- overlord
}
-- controls how the ability is issued
-- 0 = point, 1 = unit, 2 = instant (aoe), TODO: 3 = build structure
ai__profileAbilityOrderType = {
["O00C"] = {1,2,2,1,2}, -- grunt
["O010"] = {1,1,1,1,0}, -- shaman
["O00Q"] = {0,0,0,0,0}, -- tauren
["O00R"] = {1,1,1,0,1}, -- crypt fiend
["O00H"] = {0,1,2,0,0}, -- gargoyle
["O00E"] = {1,1,1,0,0}, -- footman
["O012"] = {1,2,2,0,0}, -- knight
["O00T"] = {1,2,2,2,0}, -- spell breaker
["E000"] = {1,0,2,0,0}, -- huntress
["O00M"] = {1,1,1,0,0}, -- priest
["O00J"] = {0,0,0,0,0}, -- eredar warlock
["O00S"] = {1,2,2,1,0}, -- fel orc warlock
["O013"] = {0,0,0,1,0}, -- hydra
["O014"] = {0,0,0,0,2}, -- tuskarr healer
["O016"] = {0,1,1,2,0} -- overlord
}
-- controls how the unit should use an ability vs friend and foe
-- 0 = enemy, 1 = friend, 2 = both, 3 = crowd control, 4 = self buff
ai__profileAbilityFriendFoe = {
["O00C"] = {0,4,4,0,0}, -- grunt
["O010"] = {0,1,0,1,0}, -- shaman
["O00Q"] = {0,0,0,0,0}, -- tauren
["O00R"] = {1,1,3,0,1}, -- crypt fiend
["O00H"] = {0,0,0,0,0}, -- gargoyle
["O00E"] = {0,1,0,0,0}, -- footman
["O012"] = {0,0,0,0,0}, -- knight
["O00T"] = {0,0,0,0,0}, -- spell breaker
["E000"] = {0,0,0,0,0}, -- huntress
["O00M"] = {1,0,1,0,0}, -- priest
["O00J"] = {0,0,0,0,0}, -- eredar warlock
["O00S"] = {0,0,0,0,0}, -- fel orc warlock
["O013"] = {0,0,0,0,0}, -- hydra
["O014"] = {0,0,0,0,0}, -- tuskarr healer
["O016"] = {0,1,0,0,0} -- overlord
}
-- TODO: NOT IMPLEMENTED:
-- controls how the a.i. should position themselves
-- melee, ranged, caster, healer (future TODO: builder)
ai__profileHeroAttitude = {
["O00C"] = "melee", -- grunt
["O010"] = "caster", -- shaman
["O00Q"] = "melee", -- tauren
["O00R"] = "healer", -- crypt fiend
["O00H"] = "caster", -- gargoyle
["O00E"] = "melee", -- footman
["O012"] = "melee", -- knight
["O00T"] = "melee", -- spell breaker
["E000"] = "ranged", -- huntress
["O00M"] = "healer", -- priest
["O00J"] = "caster", -- eredar warlock
["O00S"] = "caster", -- fel orc warlock
["O013"] = "caster", -- hydra
["O014"] = "healer", -- tuskarr healer
["O016"] = "melee" -- overlord
}
-- controls what ultimate agent learns at level 10
-- add raw code for ultimate
ai__profileAbilityUltimate = {
["O00C"] = "A00F", -- grunt
["O010"] = "A034", -- shaman
["O00Q"] = "A01X", -- tauren
["O00R"] = "A023", -- crypt fiend
["O00H"] = "A00V", -- gargoyle
["O00E"] = "A00M", -- footman
["O012"] = "A03B", -- knight
["O00T"] = "A02J", -- spell breaker
["E000"] = "A00K", -- huntress
["O00M"] = "A019", -- priest
["O00J"] = "A014", -- eredar warlock
["O00S"] = "A02F", -- fel orc warlock
["O013"] = "A03G", -- hydra
["O014"] = "A03I", -- tuskarr healer
["O016"] = "A03W" -- overlord
}
-- add item raw codes to this table for agent to acquire
ai__profileItemTable = {
[1] = {},
[2] = {},
[3] = {},
[4] = {},
[5] = {},
[6] = {},
[7] = {},
[8] = {},
[9] = {},
[10] = {}
}
-- !! lua has base index of 1 !!
-- grabbing values example: ai__profileAbilityOrderString.O00C[3] = taunt
------------------------------------
--config----------------------------
ai__combatEngageHeroRange__r = 650.0 -- range at which agent will try to target an enemy or ally
ai__combatDelayDuration__r = 2.44 -- the delay before the a.i. will auto attack enemy heroes in combat phase (try to keep just higher than ability cadence)
------------------------------------
ai__combatIncrement = {1,1,1,1,1,1,1,1,1,1} -- cycles through ability priority; defaulted to 1 (increments then resets to 1)
end
-- @n = player to command
-- @bool = should the agent target a hero? set to false to target nearby minions e.g. wave-clear heroes
function DAI__InitiateCombatBasic(n, heroBool)
if (ai__combatIncrement[n] > 5) then ai__combatIncrement[n] = 1 end -- reset possible ability cycle
local id = ai__heroId[n]
--DebugMsg("initiating combat, with hero id " .. id)
local orderId = ai__profileAbilityOrderPriority[id][ai__combatIncrement[n]] -- grab the array position of orderString, type, etc. based on order priority
--DebugMsg(orderId)
local orderString = ai__profileAbilityOrderString[id][orderId]
--DebugMsg(orderString)
if (orderString ~= "" and orderId ~= nil) then -- don't run if the ability is a passive or disabled
if (heroBool) then
local FriendOrFoe = ai__profileAbilityFriendFoe[id][orderId]
local orderType = ai__profileAbilityOrderType[id][orderId]
if (orderType == 0) then -- target point
--DebugMsg("found target point ability for " .. tostring(n))
local bool = false
if (FriendOrFoe == 1) then bool = true else DAI__CombatDelayedAttackUnit(n,unit) end -- true if ally target
local unit = DAI__CombatGetNearbyHero(n, bool)
if (unit ~= nil) then
IssuePointOrder(udg_playerHero[n],orderString,GetUnitX(unit),GetUnitY(unit))
end
DAI__CombatDelayedAttackUnit(n,unit)
elseif (orderType == 1) then -- unit
--DebugMsg("found unit ability for " .. tostring(n))
local unit
local bool = false
if (FriendOrFoe == 1) then bool = true else DAI__CombatDelayedAttackUnit(n,unit) end -- true if ally target
unit = DAI__CombatGetNearbyHero(n, bool)
IssueTargetOrder(udg_playerHero[n],orderString,unit)
DAI__CombatDelayedAttackUnit(n,unit)
elseif (orderType == 2) then -- instant / stomp
--DebugMsg("found instant ability for " .. tostring(n))
local unit = DAI__CombatGetNearbyHero(n, false)
if (unit ~= nil) then
IssueImmediateOrder(udg_playerHero[n],orderString)
end
-- TODO: elseif (ai__profileAbilityOrderType[id][orderId] == 3) then -- build structure
-- orderFunction = function(u,s)
-- IssueBuildOrder (unit whichPeon, string unitToBuild, real x, real y)
-- end
end
else
if (ai__combatIncrement[n] ~= 4) then -- don't cast ultimates on minions
local FriendOrFoe = ai__profileAbilityFriendFoe[id][orderId]
local orderType = ai__profileAbilityOrderType[id][orderId]
if (orderType == 0) then -- target point
--DebugMsg("found target point ability for " .. tostring(n))
local loc
local bool = false
if (FriendOrFoe == 1) then bool = true else DAI__CombatDelayedAttackUnit(n,unit) end -- true if ally target
local unit = DAI__CombatGetNearbyUnit(n, bool)
if (unit ~= nil) then
IssuePointOrder(udg_playerHero[n],orderString,GetUnitX(unit),GetUnitY(unit))
end
DAI__CombatDelayedAttackUnit(n,unit)
elseif (orderType == 1) then -- unit
--DebugMsg("found unit ability for " .. tostring(n))
local unit
local bool = false
if (FriendOrFoe == 1) then bool = true else DAI__CombatDelayedAttackUnit(n,unit) end -- true if ally target
unit = DAI__CombatGetNearbyUnit(n, bool)
IssueTargetOrder(udg_playerHero[n],orderString,unit)
DAI__CombatDelayedAttackUnit(n,unit)
elseif (orderType == 2) then -- instant / stomp
--DebugMsg("found instant ability for " .. tostring(n))
local unit = DAI__CombatGetNearbyUnit(n, false)
if (unit ~= nil) then
IssueImmediateOrder(udg_playerHero[n],orderString)
end
end
end
end
end
ai__combatIncrement[n] = ai__combatIncrement[n] + 1 -- cycle through possible abilities to use
end
-- get a nearby hero
-- @n = player number to check for; returns unit
-- @bool = set to true to grab ally, false to grab enemy
function DAI__CombatGetNearbyHero(n, bool)
local g = CreateGroup()
local size = 0
local p = ConvertedPlayer(n)
local u = nil
if (bool) then
GroupEnumUnitsInRange(g, GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]), ai__combatEngageHeroRange__r, Condition( function() return IsUnitAlly(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
else
GroupEnumUnitsInRange(g, GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]), ai__combatEngageHeroRange__r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and not BlzIsUnitInvulnerable(GetFilterUnit()) and (not IsUnitDeadBJ(GetFilterUnit())) and (IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
end
size = BlzGroupGetSize(g)
if (size > 0) then
u = GroupPickRandomUnit(g)
end
DestroyGroup(g)
return u
end
-- get a nearby unit
-- @n = player number to check for; returns unit
-- @bool = set to true to grab ally, false to grab enemy
function DAI__CombatGetNearbyUnit(n, bool)
local g = CreateGroup()
local size = 0
local p = ConvertedPlayer(n)
local u = nil
if (bool) then
GroupEnumUnitsInRange(g, GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]), ai__combatEngageHeroRange__r, Condition( function() return IsUnitAlly(GetFilterUnit(),p) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
else
GroupEnumUnitsInRange(g, GetUnitX(udg_playerHero[n]),GetUnitY(udg_playerHero[n]), ai__combatEngageHeroRange__r, Condition( function() return IsUnitEnemy(GetFilterUnit(),p) and not BlzIsUnitInvulnerable(GetFilterUnit()) and (not IsUnitDeadBJ(GetFilterUnit())) and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)) end ) )
end
size = BlzGroupGetSize(g)
if (size > 0) then
u = GroupPickRandomUnit(g)
end
DestroyGroup(g)
return u
end
-- @n = player
-- @unit = unit to attack after a short delay
function DAI__CombatDelayedAttackUnit(n, unit)
TimerStart(NewTimer(n),ai__combatDelayDuration__r,false,function()
IssueTargetOrder(udg_playerHero[n],"attack",unit)
ReleaseTimer()
end)
end
-- @n = player
-- @unit = learn eligible ultimate for this unit
function DAI__LevelUpLearnUltimate(n)
local ultId = ai__profileAbilityUltimate[ai__heroId[n]]
SelectHeroSkill(udg_playerHero[n], FourCC(ultId))
end
-- queue item creation for the agent when they level up
-- @n = player
function DAI__LevelUpGrantItem(n)
if (not udg_playerIsDead[n]) then
DAI__LevelUpCreateItem(n)
else
TimerStart(NewTimer(n),0.99,true,function() -- cannot add to dead heroes, so we run a timer to wait for respawn
if (not udg_playerIsDead[n]) then
DAI__LevelUpCreateItem(n)
ReleaseTimer()
end
end)
end
end
-- create item for the agent from possible item pool
-- @n = player
function DAI__LevelUpCreateItem(n)
local tempTable = ai__profileItemTable[n] -- grab nested item table so # works
local roll = math.random(1,#tempTable) -- choose item based on max table size
local itemId = ai__profileItemTable[n][roll]
local item = CreateItem(FourCC(itemId),1,1)
SetItemPlayer(item, ConvertedPlayer(n), false)
UnitAddItemSwapped(item,udg_playerHero[n])
table.remove(ai__profileItemTable[n],roll) -- remove from table using roll as index
end
LuaMissileTimer = NewTimer() -- global missile Timer.
LuaMissileCadence = .03 -- how fast the missile updates and checks for collision (higher = less accurate).
LuaMissileStack = {} -- where instances of missiles are stored.
LuaMissile = {} -- initialize class table.
function LuaMissile.create(ownerUnit, landingX, landingY, distance, duration, damage, hitRadius, collides, heroOnly, allies,
excludeUnit,dmgFunc,effect,effectDmg,height,scale,timerFunc,aType,dType)
local o = {}
o.ownerUnit = ownerUnit
o.owner = GetOwningPlayer(ownerUnit)
o.landingX = landingX
o.landingY = landingY
o.missileX = GetUnitX(ownerUnit)
o.missileY = GetUnitY(ownerUnit)
o.missileZ = 0.0
o.traveled = 0.0
-- set values or defaults:
o.effect = effect or 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl'
o.effectDmg = effectDmg or 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl'
o.height = height or 60.0
o.scale = scale or 1.0
o.duration = duration or 1.5
o.distance = distance or 600.0
o.damage = damage or 100.0
o.hitRadius = hitRadius or 65.0
o.collides = collides or false
o.heroOnly = heroOnly or false
o.allies = allies or false
o.dmgFunc = dmgFunc or nil
o.timerFunc = timerFunc or nil
o.excludeUnit = excludeUnit or nil
o.aType = aType or ATTACK_TYPE_NORMAL
o.dType = dType or DAMAGE_TYPE_NORMAL
--
o.velocity = distance / ( duration / LuaMissileCadence );
o.angle = AngleBetweenPointsXY(o.missileX, o.missileY, landingX, landingY)
o.effect = AddSpecialEffect(effect, o.missileX, o.missileY)
o.searchGroup = CreateGroup()
o.damageGroup = CreateGroup()
if allies then
o.filter = LuaMissile.filterAllies
else
o.filter = LuaMissile.filterEnemies
end
BlzSetSpecialEffectScale(o.effect, scale)
LuaMissile.addToStack(o)
return o
end
-- add the missile to the stack:
-- @o = missile object key
function LuaMissile.addToStack(o)
table.insert(LuaMissileStack, o)
LuaMissile.startTimer()
end
-- start or stop the Timer based on active missiles:
function LuaMissile.startTimer()
if tablelength(LuaMissileStack) == 1 and TimerGetRemaining(LuaMissileTimer) == 0.0 then
TimerStart(LuaMissileTimer, LuaMissileCadence, true, LuaMissile.updateStack)
end
end
-- start or stop the Timer based on active missiles:
function LuaMissile.timerCheck()
if tablelength(LuaMissileStack) < 1 then
PauseTimer(LuaMissileTimer)
end
end
-- run missile update for the stack:
function LuaMissile.updateStack()
for o in pairs(LuaMissileStack) do
if LuaMissileStack[o].traveled > LuaMissileStack[o].distance then
LuaMissile.destroy(o)
elseif LuaMissileStack[o] then
LuaMissile.update(o)
end
end
end
-- move the missile:
-- @o = missile object key
function LuaMissile.update(o)
LuaMissileStack[o].traveled = LuaMissileStack[o].traveled + LuaMissileStack[o].velocity
LuaMissileStack[o].missileX,LuaMissileStack[o].missileY = PolarProjectionXY(LuaMissileStack[o].missileX, LuaMissileStack[o].missileY,
LuaMissileStack[o].velocity, LuaMissileStack[o].angle)
LuaMissileStack[o].missileZ = GetTerrainCliffLevel(LuaMissileStack[o].missileX,LuaMissileStack[o].missileY)*128 - 256 + LuaMissileStack[o].height
BlzSetSpecialEffectYaw(LuaMissileStack[o].effect, LuaMissileStack[o].angle*bj_DEGTORAD) -- update facing angle of missile
BlzSetSpecialEffectX(LuaMissileStack[o].effect, LuaMissileStack[o].missileX)
BlzSetSpecialEffectY(LuaMissileStack[o].effect, LuaMissileStack[o].missileY)
BlzSetSpecialEffectZ(LuaMissileStack[o].effect, LuaMissileStack[o].missileZ)
if LuaMissileStack[o].timerFunc ~= nil then
LuaMissileStack[o].timerFunc(o)
end
LuaMissile.collisionCheck(o)
end
-- check if missile should do damage:
-- @o = missile object key
function LuaMissile.collisionCheck(o)
GroupEnumUnitsInRange(LuaMissileStack[o].searchGroup, LuaMissileStack[o].missileX, LuaMissileStack[o].missileY, LuaMissileStack[o].hitRadius,
Condition( function() return LuaMissileStack[o].filter(o, GetFilterUnit()) and GetFilterUnit() ~= LuaMissileStack[o].excludeUnit end ) )
if BlzGroupGetSize(LuaMissileStack[o].searchGroup) > 0 then
GroupUtilsAction(LuaMissileStack[o].searchGroup, function()
if not IsUnitInGroup(LUA_FILTERUNIT,LuaMissileStack[o].damageGroup) then
LuaMissile.collision(o, LUA_FILTERUNIT) end
end)
end
end
-- deal damage to the unit:
-- @o = missile object key
-- @u = unit to damage
function LuaMissile.collision(o, u)
UnitDamageTarget(LuaMissileStack[o].ownerUnit, u, LuaMissileStack[o].damage, true, false, LuaMissileStack[o].aType, LuaMissileStack[o].dType, WEAPON_TYPE_WHOKNOWS)
if LuaMissileStack[o].dmgFunc ~= nil then
LuaMissileStack[o].dmgFunc(u, o)
end
if LuaMissileStack[o].effectDmg then
DestroyEffect(AddSpecialEffect(LuaMissileStack[o].effectDmg,GetUnitX(u),GetUnitY(u)))
end
if LuaMissileStack[o].collides then
LuaMissile.destroy(o)
else
GroupUtilsAction(LuaMissileStack[o].searchGroup, function()
GroupAddUnit(LuaMissileStack[o].damageGroup,LUA_FILTERUNIT)
GroupRemoveUnit(LuaMissileStack[o].searchGroup,LUA_FILTERUNIT)
end)
end
end
-- destroy the missile instance:
-- @o = missile object key
function LuaMissile.destroy(o)
DestroyEffect(LuaMissileStack[o].effect)
DestroyGroup(LuaMissileStack[o].damageGroup)
DestroyGroup(LuaMissileStack[o].searchGroup)
LuaMissileStack[o] = nil
LuaMissile.timerCheck()
end
-- @o = missile object key
-- @u = unit to check
function LuaMissile.filterEnemies(o, u)
return IsUnitEnemy(u,LuaMissileStack[o].owner)
and not IsUnitDeadBJ(u)
and not IsUnitType(u,UNIT_TYPE_STRUCTURE)
and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)
and not IsUnitInGroup(u, LuaMissileStack[o].damageGroup)
end
-- @o = missile object key
-- @u = unit to check
function LuaMissile.filterAllies(o, u)
return IsUnitAlly(u,LuaMissileStack[o].owner)
and not IsUnitDeadBJ(u)
and not IsUnitType(u,UNIT_TYPE_STRUCTURE)
and not IsUnitInGroup(u, LuaMissileStack[o].damageGroup)
end
LUA_KB_DATA = {} -- where we store global data for KB instances.
LUA_KB_CONFIG = {} -- default configuration data.
LUA_KB_CONFIG.collisionSize = 32.0 -- x,y offset to check for walkable pathability.
LUA_KB_CONFIG.tickRate = 0.03 -- KB timer update period. recommend to keep at .03 minimum or bugs may occur. higher values = less accurate collision.
LUA_KB_CONFIG.destroyDest = true -- should destructables be destroyed? if false, meeting a destructable will count as a collision using collisionSize.
LUA_KB_CONFIG.destPlayEffect = true -- if a destructable is destroyed, should a special effect be played? plays LUA_KB_CONFIG.effect.
LUA_KB_CONFIG.destCheckSize = 192.0 -- rect width/height which checks for destructables to destroy.
LUA_KB_CONFIG.tickFailsafe = 256 -- overly-cautious hard-break cap; if this number of timer completions is exceeded, the KB will end.
LUA_KB_CONFIG.effectTickRate = 6 -- how often the special effect occurs (1 tick = .03 sec) to control for screen quality and efficiency.
LUA_KB_CONFIG.abilCode = FourCC('Arav')-- crow ability that simulates no collision (added and removed on update).
LUA_KB_CONFIG.effect = 'Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl' -- the special effect to use under the KB unit.
LUA_KB_CONFIG.destEffect = LUA_KB_CONFIG.effect -- special effect to use on destroyed destructables.
-- @KBunit = unit to kb.
-- @KBdirectionAngle = angle of KB movement.
-- @KBdistance = distance to travel.
-- @KBpause = should it pause the unit.
-- @KBduration = (seconds) how long it takes to get from point A to B. determines velocity; velocity = (KBdistance/KBduration)/(1.0/kb.tickRate)
-- @KBcollisionFunc = [optional] function to pass that does things to the unit when they collide with terrain. pass 'id' as an argument e.g. myFunc(id).
-- @KBcustomData = [optional] = override any 'kb' data (e.g. 'velocity', 'effect', etc.) with a data table, using kb data index values as the lookup.
function BasicKnockback(KBunit, KBdirectionAngle, KBdistance, KBpause, KBduration, KBcollisionFunc, KBcustomData)
if (not IsUnitType(KBunit, UNIT_TYPE_STRUCTURE) and not IsUnitType(KBunit, UNIT_TYPE_ANCIENT)) then -- KB system hard cancellations.
local id = GetUnitUserData(KBunit) -- fetch unit index data.
local kb = {} -- temp table for readability.
if (LUA_KB_DATA[id] == nil) then -- if no KB instance is active, instantiate one.
LUA_KB_DATA[id] = {}
else
LUA_KB_DATA[id].update = true -- flag the kb timer to overwrite its local data with the new KB instance.
kb = LUA_KB_DATA[id] -- with a global table, we can simply overwrite existing KB timer data for a new trajectory.
end
if (KBcustomData ~= nil) then -- if custom data is passed, assign where matches occur.
for index, value in pairs(LUA_KB_CONFIG) do
if ( DoesTableContainIndex(KBcustomData,index) ) then
kb[index] = KBcustomData[index]
else
kb[index] = value -- assigns kb.collisionSize, kb.effect, etc.
end
end
else -- custom data does not exist, initialize defaults
for index, value in pairs(LUA_KB_CONFIG) do
kb[index] = value -- assigns kb.collisionSize, kb.effect, etc.
end
end
if (not kb.rect) then kb.rect = Rect(0, 0, kb.destCheckSize, kb.destCheckSize) end -- create destructables check rect if one doesn't already exist.
kb.unit = KBunit
kb.unitX = GetUnitX(KBunit)
kb.unitY = GetUnitY(KBunit)
kb.distance = KBdistance
kb.angle = KBdirectionAngle
kb.velocity = (KBdistance/KBduration)/(1.0/kb.tickRate) -- convert tickrate timer to distance traveled per sec (velocity).
kb.velocityOffset = kb.velocity + kb.collisionSize -- precalc pathing/dest check for efficiency.
kb.pathCheckX,kb.pathCheckY = PolarProjectionXY(kb.unitX,kb.unitY,kb.velocityOffset,kb.angle) -- check for pathing and destructables.
kb.ticks = 0 -- the number of times the timer has completed (to control special effect cadence and the forced-break safety limit).
if (KBcollisionFunc ~= nil) then
kb.collisionFunc = KBcollisionFunc -- if the collision function exists, initialize it.
else
kb.collisionFunc = nil -- default to nil.
end
if (KBpause) then
PauseUnit(kb.unit,true)
end
-- if custom data is passed for calculation overwrites, assign the new values.
-- since few overwrites are expected, we allow inefficient replacement.
if (KBcustomData ~= nil) then
for index, value in pairs(KBcustomData) do
if ( DoesTableContainIndex(kb,index) ) then
kb[index] = value
end
end
end
LUA_KB_DATA[id] = kb -- update the global kb instance data.
-- -----------------------------------------------------------------------------------------------------------------------------------------------
-- [[ KB TIMER ]] --------------------------------------------------------------------------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------------------------------------------------
if (kb.timer == nil and not LUA_KB_DATA[id].update) then -- if no timer exists, generate one.
kb.timer = TimerStart(NewTimer(),kb.tickRate,true,function()
if (LUA_KB_DATA[id].update) then -- if an overwrite event occurred, update the local timer data.
kb = LUA_KB_DATA[id]
LUA_KB_DATA[id].update = false -- allow future updates again.
end
kb.pathCheckX,kb.pathCheckY = PolarProjectionXY(kb.unitX,kb.unitY,kb.velocityOffset,kb.angle) -- increment offset to check for upcoming pathing check.
kb.ticks = kb.ticks + 1
if ( IsTerrainPathable(kb.pathCheckX,kb.pathCheckY,PATHING_TYPE_WALKABILITY) and not IsUnitType(kb.unit,UNIT_TYPE_FLYING) ) then -- check pathing.
kb.hitTerrainBool = true
if (kb.collisionFunc ~= nil) then
kb.collisionFunc(id)
end
end
-- run appropriate checks to destroy the KB instance:
if (not IsUnitType(kb.unit, UNIT_TYPE_DEAD) and not kb.hitTerrainBool and kb.distance > 0 and kb.ticks < kb.tickFailsafe ) then
-- ---------------------------- --
-- [[ run effect, move unit ]] --
-- ---------------------------- --
BasicKnockbackCheckDest(kb.pathCheckX,kb.pathCheckY,id)
kb.distance = kb.distance - kb.velocity
kb.unitX,kb.unitY = PolarProjectionXY(kb.unitX,kb.unitY,kb.velocity,kb.angle)
if (ModuloInteger(kb.ticks,kb.effectTickRate) == 0) then -- limit special effect creation for performance.
DestroyEffect(AddSpecialEffect(kb.effect,kb.unitX,kb.unitY))
end
UnitAddAbility(kb.unit,kb.abilCode)
SetUnitX(kb.unit,kb.unitX)
SetUnitY(kb.unit,kb.unitY)
UnitRemoveAbility(kb.unit,kb.abilCode)
-- ---------------------------- --
-- ---------------------------- --
else -- end effect
if (IsUnitPaused(kb.unit)) then
PauseUnit(kb.unit,false)
end
RemoveRect(kb.rect) -- cleanup
kb = nil -- cleanup
LUA_KB_DATA[id] = nil -- cleanup
ReleaseTimer()
end
end)
end
-- -----------------------------------------------------------------------------------------------------------------------------------------------
-- [[ END KB TIMER ]] ----------------------------------------------------------------------------------------------------------------------------
-- -----------------------------------------------------------------------------------------------------------------------------------------------
end
end
-- @x,y = check this coord.
-- @id = for this KB instance.
function BasicKnockbackCheckDest(x,y,id)
MoveRectTo(LUA_KB_DATA[id].rect,x,y)
if (LUA_KB_DATA[id].destroyDest) then
EnumDestructablesInRect(LUA_KB_DATA[id].rect, nil, function() BasicKnockbackDest(id) end)
else
EnumDestructablesInRect(LUA_KB_DATA[id].rect, nil, function() BasicKnockbackDestCollision(id) end)
end
end
-- destroy enum destructables.
function BasicKnockbackDest(id)
bj_destRandomCurrentPick = GetEnumDestructable() -- for efficiency, hook a disposable blizzard global.
if (GetWidgetLife(bj_destRandomCurrentPick) > .405) then -- add any desired special conditions.
if (LUA_KB_DATA[id].destPlayEffect and LUA_KB_DATA[id].destEffect ~= nil) then
DestroyEffect(AddSpecialEffect( LUA_KB_DATA[id].destEffect,
GetWidgetX(bj_destRandomCurrentPick),
GetWidgetY(bj_destRandomCurrentPick) ) )
end
SetWidgetLife(bj_destRandomCurrentPick,0)
end
end
-- find out if there is a nearby destructable to collide with.
function BasicKnockbackDestCollision(id)
bj_destRandomCurrentPick = GetEnumDestructable() -- for efficiency, hook a disposable blizzard global.
if (bj_destRandomCurrentPick ~= nil and GetWidgetLife(bj_destRandomCurrentPick) > .405) then
local x = GetWidgetX(bj_destRandomCurrentPick)
local y = GetWidgetY(bj_destRandomCurrentPick)
if (IsUnitInRangeXY(LUA_KB_DATA[id].unit,x,y,LUA_KB_DATA[id].collisionSize)) then -- only stop if collisionSize met.
LUA_KB_DATA[id].hitTerrainBool = true -- set the flag to end the KB instance.
LUA_KB_DATA[id].update = true -- pass in the flag to update the running timer.
end
end
end
LUA_KB_CONFIG.defaultBoolE = function() return
not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
end
-- :: knock back units from a center point (angle being from center point towards position of affected units)
-- @unitOwner = owner of the KB (determines enemy and ally targets)
-- @x,y = point of origination.
-- @enemyBool = set to true for enemies, false for allies.
-- @areaEffectRadius = radius to look for units.
-- @KBdistance,KBpause,KBduration,KBcollisionFunc,KBcustomData = see original readme.
-- @boolexpr = (optional) custom boolean return conditions to pass in for units to knock back e.g. IsUnitType() etc. (overwrites the enemyBool flag)
function BasicKnockbackAreaPoint(unitKBowner,x,y,enemyBool,areaEffectRadius,KBdistance,KBpause,KBduration,KBcollisionFunc,KBcustomData,boolexpr)
local g = CreateGroup()
local angle
local boolE
if (boolexpr ~= nil) then
boolE = boolexpr
else
if (enemyBool) then
boolE = function() return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(unitKBowner)) end
else
boolE = function() return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(unitKBowner)) end
end
end
GroupEnumUnitsInRange(g,x,y,areaEffectRadius, Filter(function() return boolE() and LUA_KB_CONFIG.defaultBoolE() end ) )
GroupUtilsAction(g,function()
angle = AngleBetweenPointsXY(x,y,GetUnitX(LUA_FILTERUNIT),GetUnitY(LUA_FILTERUNIT))
BasicKnockback(LUA_FILTERUNIT, angle, KBdistance, KBpause, KBduration, KBcollisionFunc, KBcustomData)
end)
DestroyGroup(g)
boolE = nil
end
-- @unit = unit to hide
-- @bool = true to hide, false to show
function RemoveFromPlay(unit, bool)
ShowUnit(unit, not bool)
SetUnitPathing(unit, not bool)
SetUnitInvulnerable(unit, bool)
if (bool) then
UnitAddAbility(unit, FourCC('Aloc'))
UnitAddAbility(unit, FourCC('Amrf'))
else
UnitRemoveAbility(unit, FourCC('Aloc'))
UnitRemoveAbility(unit, FourCC('Amrf'))
end
end
-- Arcing Text Tag v1.0.0.3 by Maker encoded to Lua
DEFINITION = 1.0/60.0
SIZE_MIN = 0.010 -- Minimum size of text
SIZE_BONUS = 0.008 -- Text size increase
TIME_LIFE = 1.0 -- How long the text lasts
TIME_FADE = 1.0 -- When does the text start to fade
Z_OFFSET = -15 -- Height above unit
Z_OFFSET_BON = 25 -- How much extra height the text gains
VELOCITY = 1.0 -- How fast the text move in x/y plane
TMR = CreateTimer()
ANGLE_RND = true -- Is the angle random or fixed
if not ANGLE_RND then
ANGLE = bj_PI/2.0 -- If fixed, specify the Movement angle of the text.
end
tt = {}
as = {} -- angle, sin component
ac = {} -- angle, cos component
ah = {} -- arc height
t = {} -- time
x = {} -- origin x
y = {} -- origin y
str = {} -- text
ic = 0 -- Instance count
rn = {} ; rn[0] = 0
next = {} ; next[0] = 0
prev = {} ; prev[0] = 0 --Needed due to Lua not initializing them.
function ArcingTextTag(s, u, ti)
local this = rn[0]
if this == 0 then
ic = ic + 1
this = ic
else
rn[0] = rn[this]
end
next[this] = 0
prev[this] = prev[0]
next[prev[0]] = this
prev[0] = this
str[this] = s
x[this] = GetUnitX(u)
y[this] = GetUnitY(u)
t[this] = ti or TIME_LIFE --HOA addition: pass in custom life span
local a
if ANGLE_RND then
a = GetRandomReal(0, 2*bj_PI)
else
a = ANGLE
end
as[this] = Sin(a)*VELOCITY
ac[this] = Cos(a)*VELOCITY
ah[this] = 0.
if IsUnitVisible(u, GetLocalPlayer()) then
tt[this] = CreateTextTag()
SetTextTagPermanent(tt[this], false)
SetTextTagLifespan(tt[this], TIME_LIFE)
SetTextTagFadepoint(tt[this], TIME_FADE)
SetTextTagText(tt[this], s, SIZE_MIN)
SetTextTagPos(tt[this], x[this], y[this], Z_OFFSET)
end
if prev[this] == 0 then
TimerStart(TMR, DEFINITION, true, function()
local this = next[0]
local p
while (this ~= 0) do
p = Sin(bj_PI*t[this])
t[this] = t[this] - DEFINITION
x[this] = x[this] + ac[this]
y[this] = y[this] + as[this]
SetTextTagPos(tt[this], x[this], y[this], Z_OFFSET + Z_OFFSET_BON * p)
SetTextTagText(tt[this], str[this], SIZE_MIN + SIZE_BONUS * p)
if t[this] <= 0.0 then
tt[this] = null
next[prev[this]] = next[this]
prev[next[this]] = prev[this]
rn[this] = rn[0]
rn[0] = this
if next[0] == 0 then
PauseTimer(TMR)
end
end
this = next[this]
end
end)
end
return this
end
--[[
===========================================================================
Lua Version
Damage Engine lets you detect, amplify, block or nullify damage. It even
lets you detect if the damage was physical or from a spell. Just reference
DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
necessary damage event data.
- Detect damage (after it was dealt to the unit): use the event "DamageEvent Equal to 1.00"
- To change damage before it is dealt: use the event "DamageModifierEvent Equal to 1.00"
- Detect spell damage: use the condition "IsDamageSpell Equal to True"
- Detect zero-damage: use the event "DamageEvent Equal to 2.00"
You can specify the DamageEventType before dealing triggered damage:
- Set NextDamageType = DamageTypeWhatever
- Unit - Cause...
You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
- If the amount is modified to negative, it will count as a heal.
- If the amount is set to 0, no damage will be dealt.
If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
GUI Vars:
Retained from 3.8 and prior:
----------------------------
unit udg_DamageEventSource
unit udg_DamageEventTarget
unit udg_EnhancedDamageTarget
group udg_DamageEventAOEGroup
integer udg_DamageEventAOE
integer udg_DamageEventLevel
real udg_DamageModifierEvent
real udg_DamageEvent
real udg_AfterDamageEvent
real udg_DamageEventAmount
real udg_DamageEventPrevAmt
real udg_AOEDamageEvent
boolean udg_DamageEventOverride
boolean udg_NextDamageType
boolean udg_DamageEventType
boolean udg_IsDamageSpell
Added in 5.0:
boolean udg_IsDamageMelee
boolean udg_IsDamageRanged
unit udg_AOEDamageSource
real udg_LethalDamageEvent
real udg_LethalDamageHP
real udg_DamageScalingWC3
integer udg_DamageEventAttackT
integer udg_DamageEventDamageT
integer udg_DamageEventWeaponT
Added in 5.1:
boolean udg_IsDamageCode
Added in 5.2:
integer udg_DamageEventArmorT
integer udg_DamageEventDefenseT
Addded in 5.3:
real DamageEventArmorPierced
real udg_DamageScalingUser
Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
attacktype array udg_CONVERTED_ATTACK_TYPE
damagetype array udg_CONVERTED_DAMAGE_TYPE
=============================================================================
--]]
do
local alarm = CreateTimer()
local alarmSet = false
--Values to track the original pre-spirit Link/defensive damage values
local canKick = true
local totem = false
local armorType = 0
local defenseType = 0
local prev = {}
--Stuff to track recursive UnitDamageTarget calls.
local eventsRun = false
local kicking = false
local stack = {}
--Added in 5.4 to silently eliminate infinite recursion.
local userTrigs = 9
local eventTrig = 0
local nextTrig = {}
local userTrig = {}
local trigFrozen = {}
--Added/re-tooled in 5.4.1 to allow forced recursion (for advanced users only).
local levelsDeep = {} --How deep the user recursion currently is.
local LIMBO = 16 --Recursion will never go deeper than LIMBO.
DamageEngine_inception= false --You must set DamageEngine_inception = true before dealing damage to utlize this.
--When true, it allows your trigger to potentially go recursive up to LIMBO.
local dreaming = false
local fischerMorrow = {} --track targets of recursion
local inceptionTrig = {} --Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
local proclusGlobal = {} --track sources of recursion
local sleepLevel = 0
--Improves readability in the code to have these as named constants.
local event = {
mod = 1,
shield = 4,
damage = 5,
zero = 6,
after = 7,
lethal = 8,
aoe = 9
}
local function runTrigs(i)
local cat = i
dreaming = true
--print("Running " .. cat)
while (true) do
i = nextTrig[i]
if (i == 0)
or (cat == event.mod and (udg_DamageEventOverride or udg_DamageEventType*udg_DamageEventType == 4))
or (cat == event.shield and udg_DamageEventAmount <= 0.00)
or (cat == event.lethal and udg_LethalDamageHP > 0.405) then
break
end
if not trigFrozen[i] then
eventTrig = i
if RunTrigger then --Added 10 July 2019 to enable FastTriggers mode.
RunTrigger(userTrig[i])
elseif IsTriggerEnabled(userTrig[i])
and TriggerEvaluate(userTrig[i]) then
TriggerExecute(userTrig[i])
end
--print("Ran " .. i)
end
end
--print("Ran")
dreaming = false
end
local function onAOEEnd()
if udg_DamageEventAOE > 1 then
runTrigs(event.aoe)
udg_DamageEventAOE = 1
end
udg_DamageEventLevel = 1
udg_EnhancedDamageTarget= nil
udg_AOEDamageSource = nil
GroupClear(udg_DamageEventAOEGroup)
end
local function afterDamage()
if udg_DamageEventPrevAmt ~= 0.00 and udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
runTrigs(event.after)
end
end
local oldUDT = UnitDamageTarget
local function finish()
if eventsRun then
--print "events ran"
eventsRun = false
afterDamage()
end
if canKick and not kicking then
local n = #stack
if n > 0 then
kicking = true
--print("Clearing Recursion: " .. n)
local i = 0
local open
repeat
sleepLevel = sleepLevel + 1
repeat
i = i + 1 --Need to loop bottom to top to make sure damage order is preserved.
open = stack[i]
udg_NextDamageType = open.type
--print("Stacking on " .. open.amount)
oldUDT(open.source, open.target, open.amount, true, false, open.attack, open.damage, open.weapon)
afterDamage()
until (i == n)
--print("Exit at: " .. i)
n = #stack
until (i == n)
--print("Terminate at: " .. i)
sleepLevel = 0
repeat
open = stack[i].trig
stack[i] = nil
proclusGlobal[open] = nil
fischerMorrow[open] = nil
trigFrozen[open] = false -- Only re-enable recursive triggers AFTER all damage is dealt.
levelsDeep[open] = 0 --Reset this stuff if the user tried some nonsense
--print("unfreezing " .. open)
i = i - 1
until (i == 0)
kicking = false
end
end
end
function UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
if udg_NextDamageType == 0 then
udg_NextDamageType = udg_DamageTypeCode
end
local b = false
if dreaming then
if amt ~= 0.00 then
-- Store triggered, recursive damage into a stack.
-- This damage will be fired after the current damage instance has wrapped up its events.
stack[#stack + 1] = {
type = udg_NextDamageType,
source = src,
target = tgt,
amount = amt,
attack = at,
damage = dt,
weapon = wt,
trig = eventTrig
}
--print("increasing damage stack: " .. #stack)
-- Next block added in 5.4.1 to allow *some* control over whether recursion should kick
-- in. Also it's important to track whether the source and target were both involved at
-- some earlier point, so this is a more accurate and lenient method than before.
DamageEngine_inception = DamageEngine_inception or inceptionTrig[eventTrig]
local sg = proclusGlobal[eventTrig]
if not sg then
sg = {}
proclusGlobal[eventTrig] = sg
end
sg[udg_DamageEventSource] = true
local tg = fischerMorrow[eventTrig]
if not tg then
tg = {}
fischerMorrow[eventTrig] = tg
end
tg[udg_DamageEventTarget] = true
if kicking and sg[src] and tg[tgt] then
if DamageEngine_inception and not trigFrozen[eventTrig] then
inceptionTrig[eventTrig] = true
if levelsDeep[eventTrig] < sleepLevel then
levelsDeep[eventTrig] = levelsDeep[eventTrig] + 1
if levelsDeep[eventTrig] >= LIMBO then
--print("freezing inception trig: " .. eventTrig)
trigFrozen[eventTrig] = true
end
end
else
--print("freezing standard trig: " .. eventTrig)
trigFrozen[eventTrig] = true
end
end
end
else
b = oldUDT(src, tgt, amt, a, r, at, dt, wt)
end
--print("setting inception to false")
DamageEngine_inception = false
udg_NextDamageType = 0
if b and not dreaming then
finish() -- Wrap up the outstanding damage instance right away.
end
return b
end
local function resetArmor()
if udg_DamageEventArmorPierced ~= 0.00 then
BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + udg_DamageEventArmorPierced)
end
if armorType ~= udg_DamageEventArmorT then
BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, armorType) --revert changes made to the damage instance
end
if defenseType ~= udg_DamageEventDefenseT then
BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, defenseType)
end
end
local function failsafeClear()
--print("Damage from " .. GetUnitName(udg_DamageEventSource) .. " to " .. GetUnitName(udg_DamageEventTarget) .. " has been messing up Damage Engine.")
--print(udg_DamageEventAmount .. " " .. " " .. udg_DamageEventPrevAmt .. " " .. udg_AttackTypeDebugStr[udg_DamageEventAttackT] .. " " .. udg_DamageTypeDebugStr[udg_DamageEventDamageT])
resetArmor()
canKick = true
totem = false
udg_DamageEventAmount = 0.00
udg_DamageScalingWC3 = 0.00
if udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
runTrigs(event.damage) --Run the normal on-damage event based on this failure.
eventsRun = true --Run the normal after-damage event based on this failure.
end
finish()
end
local function calibrateMR()
udg_IsDamageMelee = false
udg_IsDamageRanged = false
udg_IsDamageSpell = udg_DamageEventAttackT == 0 --In Patch 1.31, one can just check the attack type to find out if it's a spell.
if udg_DamageEventDamageT == udg_DAMAGE_TYPE_NORMAL and not udg_IsDamageSpell then --This damage type is the only one that can get reduced by armor.
udg_IsDamageMelee = IsUnitType(udg_DamageEventSource, UNIT_TYPE_MELEE_ATTACKER)
udg_IsDamageRanged = IsUnitType(udg_DamageEventSource, UNIT_TYPE_RANGED_ATTACKER)
if udg_IsDamageMelee and udg_IsDamageRanged then
udg_IsDamageMelee = udg_DamageEventWeaponT > 0-- Melee units play a sound when damaging
udg_IsDamageRanged = not udg_IsDamageMelee -- In the case where a unit is both ranged and melee, the ranged attack plays no sound.
end -- The Huntress has a melee sound for her ranged projectile, however it is only an issue
end --if she also had a melee attack, because by default she is only UNIT_TYPE_RANGED_ATTACKER.
end
local t1 = CreateTrigger()
TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DAMAGING)
TriggerAddCondition(t1, Filter(function()
local src = GetEventDamageSource()
local tgt = BlzGetEventDamageTarget()
local amt = GetEventDamage()
local at = BlzGetEventAttackType()
local dt = BlzGetEventDamageType()
local wt = BlzGetEventWeaponType()
--print "First damage event running"
if not kicking then
if alarmSet then
if totem then
if dt ~= DAMAGE_TYPE_SPIRIT_LINK and dt ~= DAMAGE_TYPE_DEFENSIVE and dt ~= DAMAGE_TYPE_PLANT then
-- if 'totem' is still set and it's not due to spirit link distribution or defense retaliation,
-- the next function must be called as a debug. This reverts an issue I created in patch 5.1.3.
failsafeClear()
else
totem = false
canKick = false
prev.type = udg_DamageEventType -- also store the damage type.
prev.amount = udg_DamageEventAmount
prev.preAmt = udg_DamageEventPrevAmt -- Store the actual pre-armor value.
prev.pierce = udg_DamageEventArmorPierced
prev.armor = udg_DamageEventArmorT
prev.preArm = armorType
prev.defense= udg_DamageEventDefenseT
prev.preDef = defenseType
prev.code = udg_IsDamageCode -- store this as well.
end
end
if src ~= udg_AOEDamageSource then -- Source has damaged more than once
onAOEEnd() -- New damage source - unflag everything
udg_AOEDamageSource = src
elseif tgt == udg_EnhancedDamageTarget then
udg_DamageEventLevel= udg_DamageEventLevel + 1 -- The number of times the same unit was hit.
elseif not IsUnitInGroup(tgt, udg_DamageEventAOEGroup) then
udg_DamageEventAOE = udg_DamageEventAOE + 1 -- Multiple targets hit by this source - flag as AOE
end
else
TimerStart(alarm, 0.00, false, function()
alarmSet = false --The timer has expired. Flag off to allow it to be restarted when needed.
finish() --Wrap up any outstanding damage instance
onAOEEnd() --Reset things so they don't perpetuate for AoE/Level target detection
end)
alarmSet = true
udg_AOEDamageSource = src
udg_EnhancedDamageTarget= tgt
end
GroupAddUnit(udg_DamageEventAOEGroup, tgt)
end
udg_DamageEventType = udg_NextDamageType
udg_IsDamageCode = udg_NextDamageType ~= 0
udg_DamageEventOverride = dt == nil -- Got rid of NextDamageOverride in 5.1 for simplicity
udg_DamageEventPrevAmt = amt
udg_DamageEventSource = src
udg_DamageEventTarget = tgt
udg_DamageEventAmount = amt
udg_DamageEventAttackT = GetHandleId(at)
udg_DamageEventDamageT = GetHandleId(dt)
udg_DamageEventWeaponT = GetHandleId(wt)
calibrateMR() -- Set Melee and Ranged settings.
udg_DamageEventArmorT = BlzGetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE) -- Introduced in Damage Engine 5.2.0.0
udg_DamageEventDefenseT = BlzGetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE)
armorType = udg_DamageEventArmorT
defenseType = udg_DamageEventDefenseT
udg_DamageEventArmorPierced = 0.00
udg_DamageScalingUser = 1.00
udg_DamageScalingWC3 = 1.00
if amt ~= 0.00 then
if not udg_DamageEventOverride then
runTrigs(event.mod)
-- All events have run and the pre-damage amount is finalized.
BlzSetEventAttackType(ConvertAttackType(udg_DamageEventAttackT))
BlzSetEventDamageType(ConvertDamageType(udg_DamageEventDamageT))
BlzSetEventWeaponType(ConvertWeaponType(udg_DamageEventWeaponT))
if udg_DamageEventArmorPierced ~= 0.00 then
BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) - udg_DamageEventArmorPierced)
end
if armorType ~= udg_DamageEventArmorT then
BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, udg_DamageEventArmorT) -- Introduced in Damage Engine 5.2.0.0
end
if defenseType ~= udg_DamageEventDefenseT then
BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, udg_DamageEventDefenseT) -- Introduced in Damage Engine 5.2.0.0
end
BlzSetEventDamage(udg_DamageEventAmount)
end
totem = true
-- print("Ready to deal " .. udg_DamageEventAmount)
else
runTrigs(event.zero)
canKick = true
finish()
end
return false
end))
local t2 = CreateTrigger()
TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DAMAGED)
TriggerAddCondition(t2, Filter(function()
if udg_DamageEventPrevAmt == 0.00 then
return false
end
--print "Second event running"
if totem then
totem = false --This should be the case in almost all circumstances
else
afterDamage() --Wrap up the outstanding damage instance
canKick = true
--Unfortunately, Spirit Link and Thorns Aura/Spiked Carapace fire the DAMAGED event out of sequence with the DAMAGING event,
--so I have to re-generate a buncha stuff here.
udg_DamageEventSource = GetEventDamageSource()
udg_DamageEventTarget = GetTriggerUnit()
udg_DamageEventAmount = prev.amount
udg_DamageEventPrevAmt = prev.preAmt
udg_DamageEventAttackT = GetHandleId(BlzGetEventAttackType())
udg_DamageEventDamageT = GetHandleId(BlzGetEventDamageType())
udg_DamageEventWeaponT = GetHandleId(BlzGetEventWeaponType())
udg_DamageEventType = prev.type
udg_IsDamageCode = prev.code
udg_DamageEventArmorT = prev.armor
udg_DamageEventDefenseT = prev.defense
udg_DamageEventArmorPierced= prev.pierce
armorType = prev.preArm
defenseType = prev.preDef
calibrateMR() --Apply melee/ranged settings once again.
end
resetArmor()
local r = GetEventDamage()
if udg_DamageEventAmount ~= 0.00 and r ~= 0.00 then
udg_DamageScalingWC3 = r/udg_DamageEventAmount
else
if udg_DamageEventAmount > 0.00 then
udg_DamageScalingWC3 = 0.00
else
udg_DamageScalingWC3 = 1.00
end
udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt
end
udg_DamageEventAmount = udg_DamageEventAmount*udg_DamageScalingWC3
if udg_DamageEventAmount > 0.00 then
--This event is used for custom shields which have a limited hit point value
--The shield here kicks in after armor, so it acts like extra hit points.
runTrigs(event.shield)
udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= 0.405 then
runTrigs(event.lethal) -- added 10 May 2019 to detect and potentially prevent lethal damage. Instead of
-- modifying the damage, you need to modify LethalDamageHP instead (the final HP of the unit).
udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
if udg_DamageEventType < 0 and udg_LethalDamageHP <= 0.405 then
SetUnitExploded(udg_DamageEventTarget, true) --Explosive damage types should blow up the target.
end
end
udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
end
BlzSetEventDamage(udg_DamageEventAmount) --Apply the final damage amount.
if udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
runTrigs(event.damage)
end
eventsRun = true
--print(canKick)
if udg_DamageEventAmount == 0.00 then
finish()
end
return false
end))
onGlobalInit(function()
local i
for i = 0, 6 do udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i) end
for i = 0, 26 do udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i) end
udg_AttackTypeDebugStr[0] = "SPELLS" -- ATTACK_TYPE_NORMAL in JASS
udg_AttackTypeDebugStr[1] = "NORMAL" -- ATTACK_TYPE_MELEE in JASS
udg_AttackTypeDebugStr[2] = "PIERCE"
udg_AttackTypeDebugStr[3] = "SIEGE"
udg_AttackTypeDebugStr[4] = "MAGIC"
udg_AttackTypeDebugStr[5] = "CHAOS"
udg_AttackTypeDebugStr[6] = "HERO"
udg_DamageTypeDebugStr[0] = "UNKNOWN"
udg_DamageTypeDebugStr[4] = "NORMAL"
udg_DamageTypeDebugStr[5] = "ENHANCED"
udg_DamageTypeDebugStr[8] = "FIRE"
udg_DamageTypeDebugStr[9] = "COLD"
udg_DamageTypeDebugStr[10] = "LIGHTNING"
udg_DamageTypeDebugStr[11] = "POISON"
udg_DamageTypeDebugStr[12] = "DISEASE"
udg_DamageTypeDebugStr[13] = "DIVINE"
udg_DamageTypeDebugStr[14] = "MAGIC"
udg_DamageTypeDebugStr[15] = "SONIC"
udg_DamageTypeDebugStr[16] = "ACID"
udg_DamageTypeDebugStr[17] = "FORCE"
udg_DamageTypeDebugStr[18] = "DEATH"
udg_DamageTypeDebugStr[19] = "MIND"
udg_DamageTypeDebugStr[20] = "PLANT"
udg_DamageTypeDebugStr[21] = "DEFENSIVE"
udg_DamageTypeDebugStr[22] = "DEMOLITION"
udg_DamageTypeDebugStr[23] = "SLOW_POISON"
udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
udg_DamageTypeDebugStr[26] = "UNIVERSAL"
udg_WeaponTypeDebugStr[0] = "NONE" -- WEAPON_TYPE_WHOKNOWS in JASS
udg_WeaponTypeDebugStr[1] = "METAL_LIGHT_CHOP"
udg_WeaponTypeDebugStr[2] = "METAL_MEDIUM_CHOP"
udg_WeaponTypeDebugStr[3] = "METAL_HEAVY_CHOP"
udg_WeaponTypeDebugStr[4] = "METAL_LIGHT_SLICE"
udg_WeaponTypeDebugStr[5] = "METAL_MEDIUM_SLICE"
udg_WeaponTypeDebugStr[6] = "METAL_HEAVY_SLICE"
udg_WeaponTypeDebugStr[7] = "METAL_MEDIUM_BASH"
udg_WeaponTypeDebugStr[8] = "METAL_HEAVY_BASH"
udg_WeaponTypeDebugStr[9] = "METAL_MEDIUM_STAB"
udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"
udg_DefenseTypeDebugStr[0] = "LIGHT"
udg_DefenseTypeDebugStr[1] = "MEDIUM"
udg_DefenseTypeDebugStr[2] = "HEAVY"
udg_DefenseTypeDebugStr[3] = "FORTIFIED"
udg_DefenseTypeDebugStr[4] = "NORMAL"
udg_DefenseTypeDebugStr[5] = "HERO"
udg_DefenseTypeDebugStr[6] = "DIVINE"
udg_DefenseTypeDebugStr[7] = "UNARMORED"
udg_ArmorTypeDebugStr[0] = "NONE"
udg_ArmorTypeDebugStr[1] = "FLESH"
udg_ArmorTypeDebugStr[2] = "METAL"
udg_ArmorTypeDebugStr[3] = "WOOD"
udg_ArmorTypeDebugStr[4] = "ETHEREAL"
udg_ArmorTypeDebugStr[5] = "STONE"
end)
function DamageEngine_SetupEvent(whichTrig, var, val)
--print("Setup event: " .. var)
local mx = 1
local off = 0
local ex = 0
if var == "udg_DamageModifierEvent" then --event.mod 1-4 -> Events 1-4
if (val < 3) then
ex = val + 1
end
mx = 4
elseif var == "udg_DamageEvent" then --event.damage 1,2 -> Events 5,6
mx = 2
off = 4
elseif var == "udg_AfterDamageEvent" then --event.after -> Event 7
off = 6
elseif var == "udg_LethalDamageEvent" then --event.lethal -> Event 8
off = 7
elseif var == "udg_AOEDamageEvent" then --event.aoe -> Event 9
off = 8
else
return false
end
local i
if userTrigs == 9 then
nextTrig[1] = 2
nextTrig[2] = 3
trigFrozen[2] = true
trigFrozen[3] = true
for i = 3, 9 do nextTrig[i] = 0 end
end
i = math.max(math.min(val, mx), 1) + off
--print("Root index: " .. i .. " nextTrig: " .. nextTrig[i] .. " exit: " .. ex)
repeat
val = i
i = nextTrig[i]
until (i == ex)
userTrigs = userTrigs + 1 --User list runs from index 10 and up
nextTrig[val] = userTrigs
nextTrig[userTrigs] = ex
userTrig[userTrigs] = whichTrig
levelsDeep[userTrigs] = 0
trigFrozen[userTrigs] = false
inceptionTrig[userTrigs] = false
--print("Registered " .. userTrigs .. " to " .. val)
return true
end
onRegisterVar(function(trig, var, val)
DamageEngine_SetupEvent(trig, var, math.floor(val))
end)
end
-- @unit = unit to move
-- @leapDuration = duration to get to x,y
-- @x,y = target point to leap to
-- @customTakeoffFunc = function to run when effect starts
-- @customLandingFunc = function to run when effect ends
-- returns boolean
function HeroLeapTargetPoint(unit,leapDuration,x,y,customTakeoffFunc,customLandingFunc)
local u = unit
local x1 = x
local y1 = y
local x2 = GetUnitX(u)
local y2 = GetUnitY(u)
local angle = AngleBetweenPointsXY(x2,y2,x1,y1)
local travelDistance = DistanceBetweenXY(x1,y1,x2,y2)
local velocity = travelDistance/(leapDuration/.04)
local i = (leapDuration/.04)
local abilId = GetSpellAbilityId()
if (not IsTerrainPathable(x1,y1,PATHING_TYPE_WALKABILITY)) then -- pathable, allow (pathing function returns opposite bool)
SetUnitFacing(u, angle)
SetUnitPathing(u, false)
SetUnitAnimation(u,"walk")
if (customTakeoffFunc ~= nil) then
customTakeoffFunc()
end
TimerStart(NewTimer(),0.04,true,function() -- leap
if (i > 0 and IsUnitAliveBJ(u)) then
i = i-1
x2,y2 = PolarProjectionXY(x2,y2,velocity,angle)
SetUnitX(u,x2)
SetUnitY(u,y2)
else
if (customLandingFunc ~= nil) then
customLandingFunc()
end
ResetUnitAnimation(u)
SetUnitPathing(u, true)
ReleaseTimer()
end
end)
return true
else -- not pathable, reset abil cd
SpellPackInvalidMsg(u, 'Target area is not pathable!')
TimerStart(NewTimer(),0.25,false,function() BlzEndUnitAbilityCooldown(u,abilId) end)
return false
end
end
-- Items are stored in a variable called nameItemBool[]
-- In each hero init is a table of raw codes that correspond to the itemBool position.
-- In hero spell triggers, reference nameItemBool[] to implement spell effects.
-- i.e. if the item table for Shaman has index 1 set to 'A001' and the spell it alters is Magma Burst,
-- then shamanItemBool[1] becomes true and can be used in Magma Burst to do its effects.
LUA_VAR_ITEM_TABLE = {}
LUA_VAR_ITEM_TABLE_ULT = {}
-- @pInt = player to generate trigger for (1-10)
-- @func = function to run when an item is picked up by the player
function ItemShopGenerateTrigger(pInt,func)
local trig = CreateTrigger()
TriggerRegisterPlayerUnitEventSimple(trig, Player(pInt-1), EVENT_PLAYER_UNIT_PICKUP_ITEM)
TriggerAddCondition(trig, Condition( function() return GetTriggerUnit() == udg_playerHero[pInt] end))
TriggerAddAction(trig,func)
end
-- @pInt = player to generate a shop for (1-10)
-- @t = pass in the item table array e.g. {'A001','A002', .. }
-- @f = custom effects function controlled in hero init script
-- @bool = udg_ item bool unique to the hero which gets set to true
-- @t2 = ultimates table
function ItemShopGenerateShop(pInt, t, f, bool, t2)
ItemShopCreateShops(pInt)
LUA_VAR_ITEM_TABLE[pInt] = t -- set up global table for base items
if (t2 ~= nil) then
LUA_VAR_ITEM_TABLE_ULT[pInt] = t2 -- ult items
end
for i = 1,#LUA_VAR_ITEM_TABLE[pInt] do
if (LUA_VAR_ITEM_TABLE[pInt][i] ~= "") then
AddItemToStock(udg_playerShop[pInt],FourCC(LUA_VAR_ITEM_TABLE[pInt][i]),1,1)
end
end
local func = function()
if (GetItemType(GetManipulatedItem()) == ITEM_TYPE_PERMANENT) then
local itemId = GetItemTypeId(GetManipulatedItem())
local p = GetConvertedPlayerId(GetOwningPlayer( GetTriggerUnit() ))
RemoveItemFromStock(udg_playerShop[p], itemId)
RemoveItemFromStock(udg_playerShopElite[p], itemId)
for n = 1,#LUA_VAR_ITEM_TABLE[pInt] do
if (FourCC(LUA_VAR_ITEM_TABLE[pInt][n]) == itemId) then
bool[n] = true
f(itemId)
end
end
end
end
ItemShopGenerateTrigger(pInt,func)
if (udg_aiPlayerIsAgent[pInt]) then -- if Doltish AI agent, add possible items
ai__profileItemTable[pInt] = LUA_VAR_ITEM_TABLE[pInt]
end
end
-- @pInt = player to limit max mana for throughout the game
-- @int = maximum mana
function ItemShopSetManaCap(pInt, int)
local trig = CreateTrigger()
BlzSetUnitMaxMana( udg_playerHero[pInt], int )
TriggerRegisterPlayerUnitEventSimple(trig, Player(pInt-1), EVENT_PLAYER_HERO_LEVEL)
TriggerRegisterPlayerUnitEventSimple(trig, Player(pInt-1), EVENT_PLAYER_UNIT_PICKUP_ITEM)
TriggerAddCondition(trig, Condition( function() return GetTriggerUnit() == udg_playerHero[pInt] or GetManipulatingUnit() == udg_playerHero[pInt] end))
TriggerAddAction(trig, function () BlzSetUnitMaxMana( udg_playerHero[pInt], int ) end)
return trig
end
-- @pInt = player to add ultimate items for (from LUA_VAR_ITEM_TABLE_ULT)
function ItemShopAddUltItems(pInt)
for i = 1,#LUA_VAR_ITEM_TABLE_ULT[pInt] do
AddItemToStock(udg_playerShopElite[pInt],FourCC(LUA_VAR_ITEM_TABLE_ULT[pInt][i]),1,1)
table.insert(LUA_VAR_ITEM_TABLE[pInt],LUA_VAR_ITEM_TABLE_ULT[pInt][i])
end
end
-- @pInt = player to add ultimate items for (from LUA_VAR_ITEM_TABLE_ULT)
function ItemShopAddTomeItems(pInt)
AddItemToStock(udg_playerShopElite[pInt],FourCC('I02A'),1,1)
AddItemToStock(udg_playerShopElite[pInt],FourCC('I02B'),1,1)
AddItemToStock(udg_playerShopElite[pInt],FourCC('I02C'),1,1)
end
-- @pInt = player to remove ultimate items for (from LUA_VAR_ITEM_TABLE_ULT)
function ItemShopRemoveUltItems(pInt)
for i = 1,#LUA_VAR_ITEM_TABLE_ULT[pInt] do
RemoveItemFromStock(udg_playerShopElite[pInt],FourCC(LUA_VAR_ITEM_TABLE_ULT[pInt][i]),1,1)
end
end
-- @pInt = player to create item shops for (making UI buttons be last in priority)
function ItemShopCreateShops(pInt)
local normId
local eliteId
local u
local u2
if (pInt < 6) then -- horde ids
normId = FourCC('E00L')
eliteId = FourCC('E00M')
else -- alliance ids
normId = FourCC('E00O')
eliteId = FourCC('E00N')
end
local x = GetLocationX(udg_gameStartShopPoint[pInt])
local y = GetLocationY(udg_gameStartShopPoint[pInt])
u = CreateUnit(Player(pInt-1),normId,x,y,270.0)
udg_playerShop[pInt] = u
x = GetLocationX(udg_gameStartShopPointElite[pInt])
y = GetLocationY(udg_gameStartShopPointElite[pInt])
u2 = CreateUnit(Player(pInt-1),eliteId,x,y,270.0)
udg_playerShopElite[pInt] = u2
SetUnitVertexColor(u2, 100, 100, 100, 50)
u = nil
u2 = nil
end
LUA_VAR_SANC_DATA = {}
LUA_VAR_SANC_DATA.restoreGroup = CreateGroup() -- restore health.
LUA_VAR_SANC_DATA.checkGroup = CreateGroup() -- recycled check if rects are empty.
LUA_VAR_SANC_DATA.trigEnterH = CreateTrigger() -- enter Horde (some bugs with registering 2 enter events, so split in two)
LUA_VAR_SANC_DATA.trigEnterA = CreateTrigger() -- enter Alliance
LUA_VAR_SANC_DATA.trigLeave = CreateTrigger()
LUA_VAR_SANC_DATA.timer = NewTimer()
function SanctuaryPackageEnter(u)
SetUnitInvulnerable(u,true)
GroupAddUnit(LUA_VAR_SANC_DATA.restoreGroup, u)
SanctuaryPackageTimer()
end
function SanctuaryPackageLeave(u)
if udg_heroCanBecomeVulnerable[GetConvertedPlayerId(GetOwningPlayer(u))] then
SetUnitInvulnerable(u,false)
end
GroupRemoveUnit(LUA_VAR_SANC_DATA.restoreGroup, u)
end
function SanctuaryPackageRestoration(u)
if udg_heroCanRegenMana[GetConvertedPlayerId(GetOwningPlayer(u))] and GetUnitManaPercent(u) < 100.0 then
DamagePackageAddMana(u,20.0,true)
end
if GetUnitLifePercent(u) < 100.0 then
DamagePackageRestoreHealth(u,20.0,true,'Abilities\\Spells\\NightElf\\MoonWell\\MoonWellCasterArt.mdl')
end
end
function SanctuaryPackageTimer()
if BlzGroupGetSize(LUA_VAR_SANC_DATA.restoreGroup) > 0 then
TimerStart(LUA_VAR_SANC_DATA.timer,1.0,true,function()
if SanctuaryPackageTimerCheck() then
GroupUtilsAction(LUA_VAR_SANC_DATA.restoreGroup, function() SanctuaryPackageRestoration(LUA_FILTERUNIT) end)
end
GroupEnumUnitsInRect(LUA_VAR_SANC_DATA.checkGroup,gg_rct_baseHordeRegen, Condition( function() return
IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_MECHANICAL) end))
GroupEnumUnitsInRect(LUA_VAR_SANC_DATA.checkGroup,gg_rct_baseAllianceRegen, Condition( function() return
IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_MECHANICAL) end))
if BlzGroupGetSize(LUA_VAR_SANC_DATA.checkGroup < 1) then
GroupClear(LUA_VAR_SANC_DATA.restoreGroup)
SanctuaryPackageTimerCheck()
end
GroupClear(LUA_VAR_SANC_DATA.checkGroup)
end)
end
end
function SanctuaryPackageTimerCheck()
if BlzGroupGetSize(LUA_VAR_SANC_DATA.restoreGroup) < 1 then
PauseTimer(LUA_VAR_SANC_DATA.timer)
return false
else
return true
end
end
function SanctuaryPackageInit()
-- enter horde:
TriggerRegisterEnterRectSimple(LUA_VAR_SANC_DATA.trigEnterH,gg_rct_baseHordeRegen)
TriggerAddCondition(LUA_VAR_SANC_DATA.trigEnterH, Condition( function() return
not IsUnitType(GetTriggerUnit(),UNIT_TYPE_MECHANICAL)
and IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO)
and IsUnitAlly(GetTriggerUnit(),Player(12))
end))
TriggerAddAction(LUA_VAR_SANC_DATA.trigEnterH, function()
SanctuaryPackageEnter(GetTriggerUnit())
end)
-- enter alliance:
TriggerRegisterEnterRectSimple(LUA_VAR_SANC_DATA.trigEnterA,gg_rct_baseAllianceRegen)
TriggerAddCondition(LUA_VAR_SANC_DATA.trigEnterA, Condition( function() return
not IsUnitType(GetTriggerUnit(),UNIT_TYPE_MECHANICAL)
and IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO)
and IsUnitAlly(GetTriggerUnit(),Player(13))
end))
TriggerAddAction(LUA_VAR_SANC_DATA.trigEnterA, function()
SanctuaryPackageEnter(GetTriggerUnit())
end)
-- leave either:
TriggerRegisterLeaveRectSimple(LUA_VAR_SANC_DATA.trigLeave,gg_rct_baseHordeRegen)
TriggerRegisterLeaveRectSimple(LUA_VAR_SANC_DATA.trigLeave,gg_rct_baseAllianceRegen)
TriggerAddCondition(LUA_VAR_SANC_DATA.trigLeave, Condition( function() return IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO)
and IsUnitInGroup(GetTriggerUnit(),LUA_VAR_SANC_DATA.restoreGroup) end))
TriggerAddAction(LUA_VAR_SANC_DATA.trigLeave, function()
SanctuaryPackageLeave(GetTriggerUnit())
end)
end
function RunDataPreloader()
-- create temporary units to give visibility (remove revealed units from fog of war)
LUA_PRELOAD_VISIBILITY1 = CreateUnit(Player(12),FourCC('o01A'),0.0,0.0,0.0) -- temp unit to add abils to
LUA_PRELOAD_VISIBILITY2 = CreateUnit(Player(13),FourCC('o01A'),0.0,0.0,0.0) -- temp unit to add abils to
LUA_PRELOAD_TEMPUNIT = CreateUnit(Player(14),FourCC('e004'),0.0,0.0,0.0) -- temp unit to add abils to
LUA_PRELOAD_OBJECTS = #LUA_PRELOAD_ABILCODES + #LUA_PRELOAD_UNITCODES + #LUA_PRELOAD_EFFECTSTRINGS
LUA_PRELOAD_OBJECTS_LOADED = 0
LUA_PRELOAD_OBJECTS_PROGRESS = {}
LUA_PRELOAD_TXT_LOAD = ''
LUA_PRELOAD_TXT_BAR_COMPLETE = ''
LUA_PRELOAD_TXT_PROGRESS = ''
LUA_PRELOAD_TXT_COLOR = '|cff37ff37'
LUA_PRELOAD_TXT_BAR_MAP = {
[1] = '••',
[2] = '•••',
[3] = '••••',
[4] = '•••••',
[5] = '••••••',
[6] = '•••••••',
[7] = '••••••••',
[8] = '•••••••••',
[9] = '••••••••••',
[10] = '•••••••••••',
[11] = '••••••••••••',
[12] = '•••••••••••••',
[13] = '••••••••••••••',
[14] = '•••••••••••••••',
[15] = '••••••••••••••••',
[16] = '•••••••••••••••••',
[17] = '••••••••••••••••••',
[18] = '•••••••••••••••••••',
[19] = '••••••••••••••••••••',
[20] = '•••••••••••••••••••••',
[21] = '••••••••••••••••••••••',
[22] = '•••••••••••••••••••••••',
[23] = '••••••••••••••••••••••••',
[24] = '•••••••••••••••••••••••••',
[25] = '••••••••••••••••••••••••••',
[26] = '•••••••••••••••••••••••••••',
[27] = '••••••••••••••••••••••••••••',
[28] = '•••••••••••••••••••••••••••••',
[29] = '••••••••••••••••••••••••••••••',
[30] = '•••••••••••••••••••••••••••••••',
[31] = '••••••••••••••••••••••••••••••••',
[32] = '•••••••••••••••••••••••••••••••••',
[33] = '••••••••••••••••••••••••••••••••••',
[34] = '•••••••••••••••••••••••••••••••••••',
[35] = '••••••••••••••••••••••••••••••••••••',
[36] = '•••••••••••••••••••••••••••••••••••••',
[37] = '••••••••••••••••••••••••••••••••••••••',
[38] = '•••••••••••••••••••••••••••••••••••••••',
[39] = '••••••••••••••••••••••••••••••••••••••••',
[40] = '•••••••••••••••••••••••••••••••••••••••••',
[41] = '••••••••••••••••••••••••••••••••••••••••••',
[42] = '•••••••••••••••••••••••••••••••••••••••••••',
[43] = '••••••••••••••••••••••••••••••••••••••••••••',
[44] = '•••••••••••••••••••••••••••••••••••••••••••••',
[45] = '••••••••••••••••••••••••••••••••••••••••••••••',
[46] = '•••••••••••••••••••••••••••••••••••••••••••••••',
[47] = '••••••••••••••••••••••••••••••••••••••••••••••••',
[48] = '•••••••••••••••••••••••••••••••••••••••••••••••••',
[49] = '••••••••••••••••••••••••••••••••••••••••••••••••••',
[50] = '•••••••••••••••••••••••••••••••••••••••••••••••••••'
}
LUA_PRELOAD_TXT_BAR_MAP_SIZE = #LUA_PRELOAD_TXT_BAR_MAP
udg_UnitIndexerEnabled = false -- turn off indexer while units are made
--
-- preload abilities by raw code
--
local max = #LUA_PRELOAD_ABILCODES
local loop = 1
TimerStart(NewTimer(),0.01,true,function()
LUA_PRELOAD_TXT_LOAD = "|cff00b1ffLoading " .. LUA_PRELOAD_ABILCODES[loop] .. "|r"
UnitAddAbilityBJ( FourCC(LUA_PRELOAD_ABILCODES[loop]), LUA_PRELOAD_TEMPUNIT )
UnitRemoveAbilityBJ( FourCC(LUA_PRELOAD_ABILCODES[loop]), LUA_PRELOAD_TEMPUNIT )
LUA_PRELOAD_OBJECTS_LOADED = LUA_PRELOAD_OBJECTS_LOADED + 1
loop = loop + 1
RunDataPreloaderProgress()
if (loop >= #LUA_PRELOAD_ABILCODES) then
RemoveUnit(LUA_PRELOAD_TEMPUNIT)
--
-- preload units by raw code
--
max = #LUA_PRELOAD_UNITCODES
loop = 1
TimerStart(NewTimer(),0.01,true,function()
LUA_PRELOAD_TXT_LOAD = "|cff00b1ffLoading " .. LUA_PRELOAD_UNITCODES[loop] .. "|r"
RemoveUnit( CreateUnit(Player(14),FourCC(LUA_PRELOAD_UNITCODES[loop]),0.0,0.0,0.0) )
LUA_PRELOAD_OBJECTS_LOADED = LUA_PRELOAD_OBJECTS_LOADED + 1
loop = loop + 1
RunDataPreloaderProgress()
if (loop >= #LUA_PRELOAD_UNITCODES) then
--
-- preload effects by string
--
max = #LUA_PRELOAD_EFFECTSTRINGS
loop = 1
TimerStart(NewTimer(),0.01,true,function()
LUA_PRELOAD_TXT_LOAD = "|cff00b1ffLoading " .. string.sub(LUA_PRELOAD_EFFECTSTRINGS[loop],1,24) .. "|r.."
Preload(LUA_PRELOAD_EFFECTSTRINGS[loop])
LUA_PRELOAD_OBJECTS_LOADED = LUA_PRELOAD_OBJECTS_LOADED + 1
loop = loop + 1
RunDataPreloaderProgress()
if (loop >= #LUA_PRELOAD_EFFECTSTRINGS) then
RunDataPreloaderResume() -- continue after coroutine ends
ReleaseTimer()
end
end)
ReleaseTimer()
end
end)
ReleaseTimer()
end
end)
end
function RunDataPreloaderResume()
-- preload forest camps
if (TriggerEvaluate(gg_trg_Forest_Camps_Init)) then
TriggerExecute(gg_trg_Forest_Camps_Init)
LUA_PRELOAD_TXT_LOAD = "|cff64ff64Forest Camps Preloaded|r"
RunDataPreloaderProgress()
else
print("Error: Forest Camps Failed to Load")
end
-- loading complete, run post-map init trigger
LUA_PRELOAD_TXT_LOAD = "|cff64ff64Loading Complete|r"
if (TriggerEvaluate(gg_trg_Map_Loaded)) then
TriggerExecute(gg_trg_Map_Loaded)
else
udg_victoryPlayer = Player(13)
TriggerExecute(gg_trg_Victory)
print("Error: Map Load Failed, please report to map author: failed to load 'gg_trg_Map_Loaded'")
end
udg_UnitIndexerEnabled = true -- re-enable unit indexer
-- cleanup
RemoveUnit(LUA_PRELOAD_VISIBILITY1)
RemoveUnit(LUA_PRELOAD_VISIBILITY2)
LUA_PRELOAD_UNITCODES = nil
LUA_PRELOAD_ABILCODES = nil
LUA_PRELOAD_EFFECTSTRINGS = nil
LUA_PRELOAD_VISIBILITY1 = nil
LUA_PRELOAD_VISIBILITY2 = nil
LUA_PRELOAD_TEMPUNIT = nil
LUA_PRELOAD_TXT_LOAD = nil
LUA_PRELOAD_TXT_BAR_COMPLETE = nil
LUA_PRELOAD_TXT_BAR_INCOMPLETE = nil
LUA_PRELOAD_TXT_PROGRESS = nil
LUA_PRELOAD_TXT_BAR_MAP = nil
LUA_PRELOAD_TXT_BAR_MAP_SIZE = nil
LUA_PRELOAD_TXT_COLOR = nil
LUA_PRELOAD_OBJECTS_LOADED = nil
LUA_PRELOAD_OBJECTS = nil
LUA_PRELOAD_OBJECTS_PROGRESS = nil
end
function RunDataPreloaderProgress()
-- fetch index based on % objects loaded:
LUA_PRELOAD_OBJECTS_PROGRESS[1] = math.floor((LUA_PRELOAD_OBJECTS_LOADED/LUA_PRELOAD_OBJECTS)*LUA_PRELOAD_TXT_BAR_MAP_SIZE)
-- fetch reverse for progress not yet done:
LUA_PRELOAD_OBJECTS_PROGRESS[2] = LUA_PRELOAD_TXT_BAR_MAP_SIZE - LUA_PRELOAD_OBJECTS_PROGRESS[1]
LUA_PRELOAD_TXT_BAR_COMPLETE = LUA_PRELOAD_TXT_BAR_MAP[LUA_PRELOAD_OBJECTS_PROGRESS[1]]
LUA_PRELOAD_TXT_BAR_INCOMPLETE = LUA_PRELOAD_TXT_BAR_MAP[LUA_PRELOAD_OBJECTS_PROGRESS[2]]
LUA_PRELOAD_TXT_PROGRESS = LUA_PRELOAD_TXT_COLOR .. LUA_PRELOAD_TXT_BAR_COMPLETE .. '|r|cffc8c8c7' .. LUA_PRELOAD_TXT_BAR_INCOMPLETE .. '|r'
ClearTextMessages()
print(LUA_PRELOAD_TXT_LOAD)
print(LUA_PRELOAD_TXT_PROGRESS)
end
LUA_PRELOAD_ABILCODES = {
'A07P',
'A07O',
'A07M',
'A07J',
'A07K',
'A07D',
'A009',
'A007',
'A00H',
'A00O',
'A015',
'A00Y',
'A008',
'A01I',
'A00Q',
'A00X',
'A018',
'A01M',
'A01B',
'A01Q',
'A01J',
'A00L',
'A01Y',
'A01Z',
'A01G',
'A026',
'A025',
'A01H',
'A02A',
'A01V',
'A00V',
'A00W',
'A02K',
'A02L',
'A02M',
'A00G',
'A02N',
'A00D',
'A00E',
'A02S',
'A028',
'A011',
'A014',
'A02T',
'A02W',
'A02X',
'A035',
'A033',
'A03H',
'A03O',
'A03N',
'A03P',
'A034',
'A03Q',
'A03R',
'A03S',
'A03X',
'A02Y',
'A032',
'A03Y',
'A03Z',
'A040',
'A03B',
'A00R',
'A01P',
'A041',
'A00A',
'A042',
'A00B',
'A000',
'A022',
'A024',
'A043',
'A00U',
'A037',
'A045',
'A027',
'A00M',
'A029',
'A017',
'A01D',
'A01E',
'A02P',
'A046',
'A00J',
'A047',
'A001',
'A005',
'A006',
'A048',
'A049',
'A01N',
'A01O',
'A03M',
'A03J',
'A03K',
'A03L',
'A04A',
'A04B',
'A04C',
'A03D',
'A03E',
'A03F',
'A03C',
'A04D',
'A04F',
'A04E',
'A04H',
'A02B',
'A02F',
'A013',
'A010',
'A00Z',
'A02D',
'A019',
'A016',
'A03I',
'A02C',
'A02E',
'A039',
'A03G',
'A036',
'A02Z',
'A01C',
'A04I',
'A02U',
'A02V',
'A04J',
'A030',
'A031',
'A04K',
'A04L',
'A01W',
'A04M',
'A04G',
'A01X',
'A04N',
'A04O',
'A00C',
'A04Q',
'A04P',
'A00F',
'A04R',
'A020',
'A04S',
'A04T',
'A04U',
'A04V',
'A023',
'A04W',
'A021',
'A04X',
'A00T',
'A00S',
'A04Z',
'A050',
'A051',
'A04Y',
'A01L',
'A01K',
'A01F',
'A044',
'A038',
'A052',
'A053',
'A057',
'A055',
'A056',
'A02J',
'A058',
'A03A',
'A02O',
'A02Q',
'A02R',
'A05A',
'A05B',
'A05C',
'A054',
'A05D',
'A05E',
'A02H',
'A05F',
'A02I',
'A059',
'A02G',
'A05G',
'A05H',
'A05I',
'A05J',
'A05L',
'A01A',
'A05K',
'A05M',
'A05N',
'A00N',
'A00P',
'A00I',
'A05P',
'A003',
'A01U',
'A05O',
'A01S',
'A05R',
'A01T',
'A05Q',
'A01R',
'A002',
'A004',
'A05S',
'A05T',
'A00K',
'A05V',
'A05W',
'A05X',
'A05U',
'A05Y',
'A012',
'A03T',
'A03U',
'A03V',
'A03W',
'A05Z',
'A060',
'A062',
'A063',
'A064',
'A065',
'A068',
'A06F',
'A06G',
'A06P',
'A06V',
'A072',
'A073',
'A074',
'A075',
'A076'
}
LUA_PRELOAD_UNITCODES = {
'e00V',
'h00G',
'e00d',
'e00H',
'o019',
'u007',
'u008',
'u009',
'u00E',
'e00P',
'e001',
'e004',
'h00E',
'o018',
'o017',
'o000',
'o00A',
'o00B',
'o00Y',
'o00D',
'n003',
'n000',
'h000',
'h006',
'h007',
'o00Z',
'n015',
'n014',
'n00Y',
'n00M',
'n00N',
'n00O',
'n00P',
'n00Q',
'n00R',
'n00S',
'n00T',
'n00V',
'n00U',
'n00W',
'n00X',
'n00Z',
'n010',
'n011',
'n012',
'n013',
'n00H',
'n018',
'n01A',
'h00B',
'o005',
'E00L',
'E00O',
'O00W',
'H00D',
'o00X',
'h00C',
'n00K',
'O00O',
'O010',
'O00R',
'O00P',
'O00M',
'O00J',
'O013',
'O014',
'O016',
'o011',
'e00E',
'O00Q',
'e00G',
'e00F',
'u00D',
'e006',
'O00H',
'u000',
'u004',
'u003',
'u00B',
'e003',
'u00A',
'u006',
'u005',
'u002',
'u00F',
'u00C',
'O012',
'e00I',
'o00G',
'e00Q',
'e008',
'e009',
'e00B',
'e00C',
'e00A',
'e00D',
'e00K',
'e00J',
'o015',
'n01B',
'O00S',
'e007',
'o00K',
'o00L',
'n00L',
'n00J',
'E00N',
'E00M',
'n017',
'n004',
'O00T',
'O00E',
'O00C',
'O00U',
'O00V',
'E000',
'o001',
'h001',
'h002',
'n002',
'n001',
'o002',
'o004',
'o003',
'h00A',
'h009',
'o006',
'h004',
'o007',
'h003',
'o008',
'h005',
'o009',
'e002',
'n01E',
'n01D',
'o00F',
'h008',
'n01C',
'n016',
'e005',
'e00R',
'n01F',
'e00S',
'e00U'
}
LUA_PRELOAD_EFFECTSTRINGS = {
"Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdl",
"Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl",
"Objects\\Spawnmodels\\Other\\BeastmasterBlood\\BeastmasterBlood.mdl",
"Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl",
"UI\\Minimap\\MiniMap-Boss.mdl",
"Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTo.mdl",
"Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl",
"UI\\Minimap\\Minimap-QuestObjectiveBonus.mdl",
"Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl",
"Abilities\\Spells\\Items\\AIfb\\AIfbSpecialArt.mdl",
"Abilities\\Spells\\Items\\AIlb\\AIlbTarget.mdl",
"Abilities\\Weapons\\Bolt\\BoltImpact.mdl",
"Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl",
"Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",
"Doodads\\Terrain\\RockChunks\\RockChunks2.mdl",
"Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",
"Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",
"Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl",
"Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl",
"Abilities\\Spells\\Items\\RitualDagger\\RitualDaggerTarget.mdl",
"Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl",
"Abilities\\Spells\\Undead\\Web\\WebTarget.mdl",
"Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodFootman.mdl",
"Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl",
"Abilities\\Spells\\Items\\AIso\\AIsoTarget.mdl",
"Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl",
"Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl",
"Abilities\\Weapons\\Mortar\\MortarMissile.mdl",
"Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl",
"Doodads\\Cityscape\\Props\\MagicRunes\\MagicRunes0.mdl",
"Abilities\\Spells\\Undead\\UnholyFrenzy\\UnholyFrenzyTarget.mdl",
"Abilities\\Spells\\Items\\AIre\\AIreTarget.mdl",
"Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl",
"Abilities\\Weapons\\Bolt\\BoltImpact.mdl",
"Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl",
"Doodads\\Cityscape\\Props\\MagicRunes\\MagicRunes1.mdl",
"Doodads\\Cityscape\\Props\\MagicRunes\\MagicRunes0.mdl",
"Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl",
"Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl",
"Abilities\\Spells\\Items\\AIsm\\AIsmTarget.mdl",
"Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl",
"Abilities\\Spells\\Items\\AIfb\\AIfbSpecialArt.mdl",
"Units\\NightElf\\Wisp\\WispExplode.mdl",
"Abilities\\Spells\\Other\\Drain\\ManaDrainCaster.mdl",
"Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl",
"Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl",
"Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl",
"Abilities\\Spells\\Orc\\EtherealForm\\SpiritWalkerChange.mdl",
"Abilities\\Weapons\\Bolt\\BoltImpact.mdl",
"Abilities\\Weapons\\WitchDoctorMissile\\WitchDoctorMissile.mdl",
"Abilities\\Spells\\NightElf\\Starfall\\StarfallCaster.mdl",
"Objects\\Spawnmodels\\NightElf\\NEDeathSmall\\NEDeathSmall.mdl",
"Abilities\\Weapons\\DragonHawkMissile\\DragonHawkMissile.mdl",
"Doodads\\Icecrown\\Terrain\\ClearIceRock\\ClearIceRock3.mdl",
"Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl",
"Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl",
"Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",
"war3mapImported\\Ember.mdx",
"Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl",
"war3mapImported\\Pillar of Flame Orange.mdx",
"war3mapImported\\Ember Blue.mdx",
"Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl",
"war3mapImported\\Pillar of Flame Blue.mdx",
"Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl",
"war3mapImported\\Ember Green.mdx",
"Abilities\\Spells\\Undead\\PlagueCloud\\PlagueCloudCaster.mdl",
"Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl",
"war3mapImported\\Pillar of Flame Green.mdx",
"Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl",
"war3mapImported\\Nether Blast I.mdx",
"Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl",
"Abilities\\Spells\\Undead\\UnholyAura\\UnholyAura.mdl",
"Abilities\\Spells\\Undead\\Darksummoning\\DarkSummonTarget.mdl",
"Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl",
"Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl",
"Objects\\Spawnmodels\\Demon\\DemonLargeDeathExplode\\DemonLargeDeathExplode.mdl",
"Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl",
"war3mapImported\\EnergyBurst_Portrait.mdx",
"Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl",
"Abilities\\Spells\\NightElf\\MoonWell\\MoonWellCasterArt.mdl",
"Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl",
"UI\\Minimap\\MiniMap-CreepLoc-Small.mdl",
"Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl",
"Abilities\\Spells\\Other\\Doom\\DoomTarget.mdl",
"Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl",
"Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl",
"Abilities\\Weapons\\Bolt\\BoltImpact.mdl",
"Abilities\\Spells\\NightElf\\MoonWell\\MoonWellCasterArt.mdl",
"Abilities\\Spells\\Orc\\HealingWave\\HealingWaveTarget.mdl",
"Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl",
"Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl",
"Abilities\\Spells\\Human\\Heal\\HealTarget.mdl",
"abilities\\weapons\\huntermissile\\huntermissile.mdl",
"Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl",
"Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl",
"Abilities\\Spells\\Items\\AIam\\AIamTarget.mdl",
"Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl",
"Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl",
"Abilities\\Spells\\Other\\AcidBomb\\BottleImpact.mdl",
"Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl",
"Abilities\\Spells\\Orc\\LiquidFire\\Liquidfire.mdl",
"Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl",
"Abilities\\Spells\\Human\\Avatar\\AvatarCaster.mdl"
}
do
local MAX_WASTED = 1 --Every MAX_WASTED units created after map initialization, run the "garbage collector"
local sUUD = SetUnitUserData
function SetUnitUserData(whichUnit, val) end -- disallow user from changing this value.
local function runEvent(dex, val)
local pdex = udg_UDex
udg_UDex = dex
globals.udg_UnitIndexEvent = 0.00
globals.udg_UnitIndexEvent = val
udg_UDex = pdex
end
local iFuncs = {}
function onUnitIndex(func)
iFuncs[func] = func
end
onUnitIndex(function(dex) runEvent(dex, 1.00) end)
local dFuncs = {}
function onUnitDeindex(func)
dFuncs[func] = func
end
onUnitDeindex(function(dex) runEvent(dex, 2.00) end)
local wasted = 0
local gen = 0
local active = {}
local inactive = {}
local preplaced = true
onTriggerInit(function()
local re = CreateRegion()
local r = GetWorldBounds()
RegionAddRect(re, r) RemoveRect(r)
local b = Filter(function()
if udg_UnitIndexerEnabled then -- custom addition
u = GetFilterUnit()
if GetUnitUserData(u) == 0 then
local dex
if not preplaced then -- No need to check for removed units during the beginning of the game sequence
wasted = wasted + 1
if wasted > MAX_WASTED then
local n = #active
for i = n, 1, -1 do
if GetUnitUserData(udg_UDexUnits[dex]) == 0 then
active[i] = active[n]
active[n] = nil
n = n - 1
inactive[#inactive + 1] = dex
for k, f in pairs(dFuncs) do f(dex) end -- Run the deindex event
--print("deindexed" .. dex)
end
end
wasted = 0
end
end
local n = #inactive
if n == 0 then
dex = gen + 1
gen = dex
else
dex = inactive[n]
inactive[n] = nil
end
active[#active + 1] = dex
udg_UDexUnits[dex] = u
sUUD(udg_UDexUnits[dex], dex)
udg_IsUnitPreplaced[dex] = preplaced
for k, f in pairs(iFuncs) do f(dex) end -- Run the index event
--print("indexed " .. dex)
end
return false
end
end)
TriggerRegisterEnterRegion(CreateTrigger(), re, b)
for i = bj_MAX_PLAYER_SLOTS - 1, 0, -1 do
GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
end
preplaced = false
globals.udg_UnitIndexEvent = 3.00
end)
end
-- @unit = pass in the obj unit to run a timer for, casting spells in their viscinity
-- @faction (int 0 or 1) = faction which determines the spell effect
function ObjDuelSpellGenerate(unit,f)
-- config
local spellInterval = 5.57 -- how often spell happens
local spellDelay = 1.48 -- the spell delay
local damage = 9*(udg_objRampReal*2.0) -- increment damage as game progresses
local radius = 175.0 -- radius/range of damage
--
local p = GetOwningPlayer(unit)
local counter = 0
TimerStart(NewTimer(), spellInterval, true, function()
if (not IsUnitAliveBJ(unit) or counter > 60) then
ReleaseTimer()
else
local g = CreateGroup()
local x = GetUnitX(unit)
local y = GetUnitY(unit)
GroupEnumUnitsInRange(g, x, y, 725.0, Condition( function() return DamagePackageEnemyNonSummonFilter(GetFilterUnit(), p) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
local i = 0
local u = GroupPickRandomUnit(g)
-- transform x,y to position of picked unit
x = GetUnitX(u)
y = GetUnitY(u)
-- create visual eye candy for the spell delay
local castEffect = AddSpecialEffect('Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeTarget.mdl', x, y)
BlzSetSpecialEffectScale(castEffect,0.60)
TimerStart(NewTimer(),spellDelay,false,function() -- cast after a delay
DestroyEffect(castEffect)
ObjDuelSpell(unit,x,y,damage,radius,f)
ReleaseTimer()
end)
end
DestroyGroup(g)
counter = counter + 1 -- prevent infinite timer if unit check fails
end
end)
end
-- cast the ability for the unit it is attached to
function ObjDuelSpell(unit,x,y,damage,radius,f)
local effect = ""
if (f == 0) then
effect = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
elseif (f == 1) then
effect = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
end
local i = 0
TimerStart(NewTimer(),0.11,true,function()
-- generate effects in a radius using circle math https://programming.guide/random-point-within-circle.html
local a = math.random() * 2 * 3.141 -- simplify pi to save on calc time
local r = radius * math.sqrt(math.random())
local x2 = x + (r * math.cos(a))
local y2 = y + (r * math.sin(a))
--
DamagePackageDealAOE(unit, x, y, radius, damage, false, false, '', '', effect, 1.25, true)
local e = AddSpecialEffect(effect, x2, y2)
DestroyEffect(e)
i = i+1
if (i >= 24 or udg_victoryGameIsOver == true) then
ReleaseTimer()
end
end)
end
-- @unit = heroId to do lookup for
-- @i = player number to init
function LoadHero(unit, i)
local t = GetUnitTypeId(unit)
RemoveUnitFromAllStock(t)
if (t == FourCC('O00E')) then
FootmanInit(unit, i)
elseif (t == FourCC('O00M')) then
PriestInit(unit, i)
elseif (t == FourCC('O00T')) then
SpellBreakerInit(unit, i)
elseif (t == FourCC('O00C')) then
GruntInit(unit, i)
elseif (t == FourCC('O00Q')) then
TaurenInit(unit, i)
elseif (t == FourCC('O00O')) then
AcolyteInit(unit, i)
elseif (t == FourCC('O00R')) then
CryptFiendInit(unit, i)
elseif (t == FourCC('O00H')) then
GargoyleInit(unit, i)
elseif (t == FourCC('E000')) then
HuntressInit(unit, i)
elseif (t == FourCC('O00P')) then
WispInit(unit, i)
elseif (t == FourCC('O00J')) then
EredarWarlockInit(unit, i)
elseif (t == FourCC('O00S')) then
FelOrcWarlockInit(unit, i)
elseif (t == FourCC('O010')) then
ShamanInit(unit, i)
elseif (t == FourCC('O012')) then
KnightInit(unit, i)
elseif (t == FourCC('O013')) then
HydraInit(unit, i)
elseif (t == FourCC('O014')) then
TuskarrHealerInit(unit, i)
elseif (t == FourCC('O016')) then
OverlordInit(unit, i)
elseif (t == FourCC('O01B')) then
FurbolgInit(unit, i)
elseif (t == FourCC('O01C')) then
DruidClawInit(unit, i)
elseif (t == FourCC('E00T')) then
SpiritWalkerInit(unit, i)
elseif (t == FourCC('O01F')) then
HeadhunterInit(unit, i)
elseif (t == FourCC('O01G')) then
RiflemanInit(unit, i)
elseif (t == FourCC('O01H')) then
ForestTrollWarlordInit(unit, i)
end
local s = UnitId2String(t)
s = string.gsub(s,"custom_","")
udg_ZplayerHeroId[i] = s -- for debugging
-- remove from possible a.i. heroes to prevent duplicates
if (DoesTableContainValue(ai__faction__heroes__horde,s)) then
DAI__RemoveHeroId(s, 0)
elseif (DoesTableContainValue(ai__faction__heroes__alliance,s)) then
DAI__RemoveHeroId(s, 1)
elseif (DoesTableContainValue(ai__faction__heroes__neutral,s)) then
DAI__RemoveHeroId(s, 2)
end
BlzSetHeroProperName(udg_playerShop[i],GetUnitName(unit) .. "'s Shop")
BlzSetHeroProperName(udg_playerShopElite[i],GetUnitName(unit) .. "'s Elite Shop")
end
function ShamanInit(unit, i)
ShamanItemsInit(i)
ShamanAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNShaman.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Shaman_Duplication )
EnableTrigger( gg_trg_Shaman_Magma )
EnableTrigger( gg_trg_Shaman_Lightning_Shield )
EnableTrigger( gg_trg_Shaman_Bloodlust )
EnableTrigger( gg_trg_Shaman_Overcharge )
EnableTrigger( gg_trg_Shaman_Thundercrash )
udg_shamanUnit = unit
end
function ShamanItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I00C", -- Pulsating Magma Stone (Q)
"I00D", -- Electrified Ether (W)
"I00E", -- Overloaded Crystal (E)
"I00G", -- Amplified Ward (F)
"I00F", -- Static Charm (Q)
"I02X", -- Volatile Lava Globule (Q)
"I02Y", -- Magnetized Plating (W)
"I02Z", -- Voltaic Hammer (E)
"I030", -- Claws of Surging Energy (F)
"I033", -- Stoneclaw Ossuary (W)
"I035" -- Mana-Binding Aether (E)
}
-- elite items added at level 14
local itemTableUlt = {
"I006", -- Scorching Slag (Q)
"I036", -- Scepter of Rolling Thunder (W)
"I034", -- Gauntlets of Overwhelming Power (E)
"I037", -- Hood of Echoing Elements (F)
"I032", -- Thunder King's Talisman (R)
"I031" -- Mandokir's Talisman (R)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00G")) then IncUnitAbilityLevel(udg_shamanUnit,FourCC("A031"))
elseif (itemId == FourCC("I02X")) then IncUnitAbilityLevel(udg_shamanUnit,FourCC("A036"))
elseif (itemId == FourCC("I031")) then UnitRemoveAbility(udg_shamanUnit,FourCC("A034")) UnitAddAbility(udg_shamanUnit,FourCC("A01C"))
-- BlzSetUnitAbilityCooldown( udg_shamanUnit, FourCC('A034'), 0, 45.00 )BlzSetUnitAbilityManaCost( udg_shamanUnit, FourCC('A034'), 0, 0 )
elseif (itemId == FourCC("I036")) then IncUnitAbilityLevel(udg_shamanUnit,FourCC("A030"))
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_shamanItemBool,itemTableUlt)
end
function ShamanAbilsInit(pInt)
Shaman_Thundercrash = CreateTrigger()
local Shaman_Thundercrash_f = function()
local u = GetTriggerUnit()
local x = GetLocationX(GetSpellTargetLoc())
local y = GetLocationY(GetSpellTargetLoc())
local i = 0
local r = 200.0
local scale = 1.0
local d = GetHeroStatBJ(bj_HEROSTAT_INT, u, true)*2.5
if (udg_shamanItemBool[16]) then
r = 260.0
scale = 1.15
d = d*1.5
end
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\Bolt\\BoltImpact.mdl',x,y))
TimerStart(NewTimer(),0.75,true,function() -- timer building up to effect
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\Bolt\\BoltImpact.mdl',x,y))
i = i +1
if (i >= 3) then -- 2.25 reached, run spell effect
local g = CreateGroup()
DestroyEffect(AddSpecialEffect('war3mapImported\\LightningWrath.mdx',x,y))
BlzSetSpecialEffectScale( GetLastCreatedEffectBJ(), scale )
DamagePackageDealAOE(u, x, y, r, d, false, false, '', '', 'Abilities\\Weapons\\Bolt\\BoltImpact.mdl', 1.25)
GroupEnumUnitsInRange(g, x, y, r, Condition( function() return
IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(u))
and IsUnitAliveBJ(GetFilterUnit())
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE))
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
local u2 = FirstOfGroup(g)
local id = 0
repeat
id = GetUnitUserData(u2)
if (IsUnitInGroup(u2, udg_shamanShieldDebuffGroup)) then
udg_shamanShieldUnitStack[id] = udg_shamanShieldUnitStack[id] + 5
udg_shamanShieldUnitDuration[id] = 0.0
--DestroyEffect(udg_shamanShieldDebuffEffect[id])
--udg_shamanShieldDebuffEffect[id] = AddSpecialEffectTarget('Abilities\\Spells\\Items\\AIlb\\AIlbTarget.mdl', u2, 'overhead')
end
GroupRemoveUnit(g,u2)
u2 = FirstOfGroup(g)
until (u2 == nil)
end
DestroyGroup(g)
ReleaseTimer()
end
end)
local u = nil
local x = nil
local y = nil
local i = nil
local d = nil
end
TriggerRegisterPlayerUnitEvent(Shaman_Thundercrash,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Shaman_Thundercrash, Filter( function() return GetSpellAbilityId() == FourCC('A04G') end ) )
TriggerAddAction(Shaman_Thundercrash,Shaman_Thundercrash_f)
end
function TaurenInit(unit, i)
TaurenItemsInit(i)
TaurenAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNTauren.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Tauren_Iron_Hide )
EnableTrigger( gg_trg_Tauren_Ground_Slam )
EnableTrigger( gg_trg_Tauren_Charge )
EnableTrigger( gg_trg_Tauren_Tectonic_Shift )
EnableTrigger( gg_trg_Tauren_Pulverize )
EnableTrigger( gg_trg_Tauren_Pulverize_Activate )
udg_taurenChargeHashtable = InitHashtable()
udg_taurenUnit = unit
--DisplayTextToForce( GetPlayersAll(), "Debug: Tauren Initialized")
end
function TaurenItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I00H", -- Tremor Gauntlets (Q)
"I00I", -- Hallowed Totem (W)
"I00J", -- Thunderhooves(E)
"I00K", -- Tauren Warbanner (Active)
"I00M", -- Siege Boulder (Q)
"I038", -- Arcanite Totem Casting (W)
"I03A", -- Battering Ram (E)
"I03B", -- Reflective Dragonhide (Trait)
"I03C", -- Trollskin Armor (Q)
"I03E", -- Bonebreaker Studs (W)
"I03I" -- Bullish Sprinters (E)
}
local itemTableUlt = {
"I03F", -- Earthshaker Crown (Q)
"I03D", -- Bellowing Totem (W)
"I03H", -- Onslaught Bracers (E)
"I03G", -- Curiass of Relentless Destruction (Trait)
"I03J", -- Hammer of Khaz'goroth (R)
"I039" -- Darkmoon Card: Earthquake (R)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00H")) then IncUnitAbilityLevel(udg_taurenUnit,FourCC("A01O"))
elseif (itemId == FourCC("I03A")) then IncUnitAbilityLevel(udg_taurenUnit,FourCC("A01W"))
elseif (itemId == FourCC("I03J")) then Tauren_spiritL = 2
elseif (itemId == FourCC("I00I") or itemId == FourCC("I03D")) then
udg_taurenPulverizeRequired = udg_taurenPulverizeRequired - 1
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_taurenItemBool,itemTableUlt)
end
function TaurenAbilsInit(pInt)
Tauren_SpiritStampede = CreateTrigger()
Tauren_spiritX = {} -- spirit create points
Tauren_spiritY = {}
Tauren_spiritU = {} -- spirit unit
Tauren_spiritX2 = {} -- spirit move points
Tauren_spiritY2 = {}
Tauren_spiritKB = {} -- was unit knocked back already? if so, don't allow additional
Tauren_spiritL = 1 -- what level is the ult
Tauren_spiritG = CreateGroup() -- KB check group
local Tauren_SpiritStampede_f = function()
local caster = GetTriggerUnit()
local tarX = GetSpellTargetX() -- cast loc
local tarY = GetSpellTargetY()
local casterX = GetUnitX(caster)
local casterY = GetUnitY(caster)
local distance = 800*Tauren_spiritL
local cadence = 0.5/Tauren_spiritL
local facing = AngleBetweenPointsXY(casterX,casterY,tarX,tarY)
local p = GetOwningPlayer(caster)
local ticks = 4/cadence
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*.25
if (Tauren_spiritL == 2) then dmg = dmg*1.15 end
Tauren_spiritX[1],Tauren_spiritY[1] = PolarProjectionXY(casterX,casterY,125,facing-90)
Tauren_spiritX[2],Tauren_spiritY[2] = PolarProjectionXY(casterX,casterY,250,facing-90)
Tauren_spiritX[3],Tauren_spiritY[3] = PolarProjectionXY(casterX,casterY,32,facing)
Tauren_spiritX[4],Tauren_spiritY[4] = PolarProjectionXY(casterX,casterY,125,facing+90)
Tauren_spiritX[5],Tauren_spiritY[5] = PolarProjectionXY(casterX,casterY,250,facing+90)
for i = 1,5 do
Tauren_spiritU[i] = CreateUnit(p, FourCC('o019'), Tauren_spiritX[i], Tauren_spiritY[i], facing)
Tauren_spiritX2[i],Tauren_spiritY2[i] = PolarProjectionXY(GetUnitX(Tauren_spiritU[i]),GetUnitY(Tauren_spiritU[i]),distance,facing) -- move point
SetUnitMoveSpeed(Tauren_spiritU[i],195*Tauren_spiritL)
DestroyEffect( AddSpecialEffect('Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl',Tauren_spiritX[i],Tauren_spiritY[i]) )
IssuePointOrderById(Tauren_spiritU[i], 851986, Tauren_spiritX2[i], Tauren_spiritY2[i]) -- move order
SetUnitVertexColorBJ(Tauren_spiritU[i], 45, 45, 100, 35)
UnitApplyTimedLifeBJ( 4.25, FourCC('BTLF'), Tauren_spiritU[i] )
end
local kbg = CreateGroup() -- temp group
local u -- temp unit
TimerStart(NewTimer(),cadence,true,function()
local int = 0
for i2 = 1,5 do
tarX = GetUnitX(Tauren_spiritU[i2])
tarY = GetUnitY(Tauren_spiritU[i2])
DestroyEffect( AddSpecialEffect('Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl',tarX,tarY) )
DamagePackageDealAOE(udg_taurenUnit, tarX, tarY, 175.0, dmg, false, false, '', '', 'Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl', 1.0)
GroupEnumUnitsInRange(kbg, tarX, tarY, 175.0, Condition( function() return BasicDamageExpr(p) end ) )
if (not IsUnitGroupEmptyBJ(kbg)) then
u = FirstOfGroup(kbg)
repeat -- initiate kb
if (not IsUnitInGroup(u,Tauren_spiritG)) then
GroupAddUnit(Tauren_spiritG,u)
BasicKnockback(u, facing, 300.0, false, 0.66, nil, nil)
GroupRemoveUnit(kbg, u)
u = FirstOfGroup(kbg)
end
int = int + 1
until (u == nil or int > 16)
end
end
ticks = ticks - 1 -- every cadence, reduce toward 0
if (ticks <= 0) then
for i3 = 1,5 do
RemoveUnit(Tauren_spiritU[i3])
Tauren_spiritU[i3] = nil
end
DestroyGroup(kbg)
GroupClear(Tauren_spiritG) -- units can receive kb again on next cast
ReleaseTimer()
end
end)
end
TriggerRegisterPlayerUnitEvent(Tauren_SpiritStampede,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Tauren_SpiritStampede, Filter( function() return GetSpellAbilityId() == FourCC('A04N') end ) )
TriggerAddAction(Tauren_SpiritStampede,Tauren_SpiritStampede_f)
end
function GruntInit(unit, i)
GruntItemsInit(i)
GruntAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNGrunt.blp'
LeaderboardUpdateHeroIcon(i)
udg_gruntUnit = unit
EnableTrigger( gg_trg_Grunt_Staggering_Smash )
EnableTrigger( gg_trg_Grunt_Berserker_Rage )
EnableTrigger( gg_trg_Grunt_Bloodletting )
EnableTrigger( gg_trg_Grunt_Bloody_Brawler )
udg_heroCanRegenMana[i] = false
LUA_TRG_GRUNT_MAX_MANA = ItemShopSetManaCap(i, 20)
end
function GruntItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I00L", -- 1 Fiery War Axe (Q)
"I00N", -- 2 Ragefire Coif (W)
"I00O", -- 3 Blood Pact (E)
"I00P", -- 4 Orc Joggers (F)
"I00Q", -- 5 Spiked Pauldrons (Q)
"I03M", -- 6 Ragefire Pendant (W)
"I03O", -- 7 Enchanted Troll Charm (E)
"I03P", -- 8 Burning Blade Banner (F)
"I03Q", -- 9 Skullfire Shield (Q)
"I03N", -- 10 Ragefire Spaulders (W)
"I03S" -- 11 Blood Salve (Active)
}
local itemTableUlt = {
"I03T", -- 12 Juggernaut Shako (Q)
"I03U", -- 13 Ragefire Core (W)
"I03R", -- 14 Sanguinary Orb (Passive)
"I03V", -- 15 Bracers of Insatiable Thirst (F)
"I03K", -- 16 Butcher's Cleaver (R1)
"I03L" -- 17 The Rending Blade (R2)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00N")) then IncUnitAbilityLevel(udg_gruntUnit,FourCC("A00A"))
elseif (itemId == FourCC("I00O")) then IncUnitAbilityLevel(udg_gruntUnit,FourCC("A00B")) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_gruntItemBool,itemTableUlt)
end
function GruntAbilsInit(pInt)
Grunt_Execute = CreateTrigger()
Grunt_Bloodthirst = CreateTrigger()
Grunt_Bloodthirst_Listener = CreateTrigger()
local Grunt_Execute_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local hp = BlzGetUnitMaxHP(caster)*.1
local dmg = BlzGetUnitMaxHP(caster)*.15
if (udg_gruntItemBool[17]) then
dmg = dmg *1.333
end
DamagePackageDealDirect(caster, target, dmg, 0, 0, false, '')
if (GetWidgetLife(target) < .405) then
DamagePackageDealDirect(caster, caster, hp*1.5, 0, 0, true) -- heal if unit died
DestroyEffect( AddSpecialEffect( 'Objects\\Spawnmodels\\Orc\\OrcLargeDeathExplode\\OrcLargeDeathExplode.mdl',GetUnitX(caster),GetUnitY(caster) ) )
DamagePackageAddMana(caster,10,false)
if (udg_gruntItemBool[17]) then
BlzEndUnitAbilityCooldown(GetTriggerUnit(),FourCC('A04P'))
end
end
DamagePackageDealDirect(caster, caster, hp, 0, 0, false, 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl')
caster = nil
target = nil
end
local Grunt_Bloodthirst_f = function()
local caster = GetTriggerUnit()
local hp = BlzGetUnitMaxHP(caster)*.1
DamagePackageDealDirect(caster, caster, hp, 0, 0, false, 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl')
BlzEndUnitAbilityCooldown(GetTriggerUnit(),FourCC('A00C'))
if (udg_gruntItemBool[16]) then
BlzEndUnitAbilityCooldown(GetTriggerUnit(),FourCC('A00B'))
BlzEndUnitAbilityCooldown(GetTriggerUnit(),FourCC('A00G'))
end
EnableTrigger(Grunt_Bloodthirst_Listener)
TimerStart(NewTimer(), 10.0, false, function()
DisableTrigger(Grunt_Bloodthirst_Listener)
ReleaseTimer()
end)
caster = nil
end
local Grunt_Bloodthirst_l_f = function()
local x = GetUnitX(udg_DamageEventTarget)
local y = GetUnitY(udg_DamageEventTarget)
if (IsUnitInRangeXY(udg_gruntUnit,x,y,1000.0)) then
TimerStart(NewTimer(),0.50,false,function() BlzEndUnitAbilityCooldown(udg_gruntUnit,FourCC('A00C')) ReleaseTimer() end )
DestroyEffect( AddSpecialEffect( 'Objects\\Spawnmodels\\Orc\\OrcLargeDeathExplode\\OrcLargeDeathExplode.mdl',GetUnitX(udg_gruntUnit),GetUnitY(udg_gruntUnit) ) )
end
end
TriggerRegisterPlayerUnitEvent(Grunt_Execute,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Grunt_Execute, Filter( function() return GetSpellAbilityId() == FourCC('A04P') end ) )
TriggerAddAction(Grunt_Execute,Grunt_Execute_f)
TriggerRegisterPlayerUnitEvent(Grunt_Bloodthirst,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Grunt_Bloodthirst, Filter( function() return GetSpellAbilityId() == FourCC('A04Q') end ) )
TriggerAddAction(Grunt_Bloodthirst,Grunt_Bloodthirst_f)
TriggerRegisterVariableEvent( Grunt_Bloodthirst_Listener, "udg_LethalDamageEvent", EQUAL, 1.00 )
TriggerAddCondition(Grunt_Bloodthirst_Listener, Filter( function() return (udg_DamageEventTarget ~= udg_gruntUnit
and IsUnitType(udg_DamageEventTarget,UNIT_TYPE_HERO)
and IsUnitEnemy(udg_DamageEventTarget, GetOwningPlayer(udg_gruntUnit)) )
end ) )
TriggerAddAction(Grunt_Bloodthirst_Listener,Grunt_Bloodthirst_l_f)
DisableTrigger(Grunt_Bloodthirst_Listener)
end
function SpiritWalkerInit(unit, i)
LUA_VAR_SPIRITWALKER_UNIT = unit
LUA_VAR_SPIRITWALKER_ITEMBOOL = {}
SpiritWalkerItemsInit(i)
SpiritWalkerAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNSpiritWalker.blp'
LeaderboardUpdateHeroIcon(i)
end
function SpiritWalkerItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I09H", -- 1 (Q) Bottled Spirits
"I09I", -- 2 (W) Radiant Mana Gem
"I09J", -- 3 (E) Spirit Treads
"I09K", -- 4 (Trait) Conduction Pipe
"I09L", -- 5 (Q) Wand of a Protection
"I09P", -- 6 (W) Key of Vitality
"I09Q", -- 7 (E) Ancestral Chopper
"I09W", -- 8 (Trait) Energized Herbs
"I09M", -- 9 (Q) Amulet of Mending
"I09N", -- 10 (W) Glyph of Vitality
"I09O" -- 11 (E) Ancestral Charm
}
local itemTableUlt = {
"I09S", -- 12 (Q) Cairne's Echoing Medallion
"I09X", -- 13 (W) Cairne's Triumphant Will
"I09R", -- 14 (E) Cairne's Ancestral Pendant
"I09T", -- 15 (Trait) Cairne's Enchanted Bulwark
"I09V", -- 16 (R1) Cairne's Shattered Totem
"I09U" -- 17 (R2) Cairne's Imbued Tramplers
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I09K")) then IncUnitAbilityLevel(LUA_VAR_SPIRITWALKER_UNIT,FourCC("A06P"))
elseif (itemId == FourCC("I09M")) then IncUnitAbilityLevel(LUA_VAR_SPIRITWALKER_UNIT,FourCC("A06R"))
elseif (itemId == FourCC("I09N")) then IncUnitAbilityLevel(LUA_VAR_SPIRITWALKER_UNIT,FourCC("A06Q"))
elseif (itemId == FourCC("I09O")) then IncUnitAbilityLevel(LUA_VAR_SPIRITWALKER_UNIT,FourCC("A06S"))
elseif (itemId == FourCC("I09T")) then
HeroAddStatTimer(LUA_VAR_SPIRITWALKER_UNIT, 0, 1, 45.0, 7)
HeroAddStatTimer(LUA_VAR_SPIRITWALKER_UNIT, 2, 1, 45.0, 7)
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_SPIRITWALKER_ITEMBOOL,itemTableUlt)
end
function SpiritWalkerAbilsInit(pInt)
SpiritWalker_Link = CreateTrigger()
SpiritWalker_Mend = CreateTrigger()
SpiritWalker_Shield = CreateTrigger()
SpiritWalker_Binding = CreateTrigger()
SpiritWalker_Vigor = CreateTrigger()
SpiritWalker_Stomp = CreateTrigger()
SpiritWalker_Shield_Listener = CreateTrigger()
LUA_VAR_SPIRITWALKER_SHIELD_TIMER = NewTimer()
LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT = 0
local SpiritWalker_Link_f = function ()
local caster = GetTriggerUnit()
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[8] and GetUnitLifePercent(caster) > 75.0) then
DamagePackageAddMana(caster,50,false)
end
end
local SpiritWalker_Mend_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local healing = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*2.25
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[1]) then
local g = CreateGroup()
GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),600.0,Filter(function() return
UnitHasBuffBJ(GetFilterUnit(),FourCC('Bspl'))
and IsUnitAlly(GetFilterUnit(),GetOwningPlayer(caster)) end) )
local size = BlzGroupGetSize(g)
if (size > 0) then
healing = healing*(1 + size*0.1)
end
DestroyGroup(g)
end
DamagePackageRestoreHealth(target,healing,false,nil,caster)
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[12] and caster ~= target) then
DamagePackageRestoreHealth(caster,healing,false,nil,caster)
end
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[5]) then
DamagePackageDummyTarget(caster,'A00O','innerfire')
end
DamagePackageDummyTarget(caster,'A06P','spiritlink',target)
end
local SpiritWalker_Shield_Listener_f = function()
LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT = LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT - udg_DamageEventAmount
if (LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT > 0.0 and IsUnitAliveBJ(LUA_VAR_SPIRITWALKER_UNIT)) then
mvp.IncrementAbsorbed(GetConvertedPlayerId(GetOwningPlayer(LUA_VAR_SPIRITWALKER_UNIT)), udg_DamageEventAmount)
udg_DamageEventAmount = 0.0
ArcingTextTag( "|cff00b1ffAbsorbed|r", udg_DamageEventTarget)
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[2]) then
DamagePackageAddMana(LUA_VAR_SPIRITWALKER_UNIT,5,false)
end
elseif (not IsUnitAliveBJ(LUA_VAR_SPIRITWALKER_UNIT)) then
DisableTrigger(SpiritWalker_Shield_Listener)
else
udg_DamageEventAmount = math.abs(LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT)
UnitRemoveBuffBJ( FourCC('B01T'), LUA_VAR_SPIRITWALKER_UNIT )
DisableTrigger(SpiritWalker_Shield_Listener)
end
end
local SpiritWalker_Shield_f = function()
local caster = GetTriggerUnit()
local dur = 6.0
LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*5.0
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[6]) then
LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT = LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT*1.4
end
EnableTrigger(SpiritWalker_Shield_Listener)
if (LUA_VAR_SPIRITWALKER_SHIELD_TIMER ~= nil) then ReleaseTimer(LUA_VAR_SPIRITWALKER_SHIELD_TIMER) end
LUA_VAR_SPIRITWALKER_SHIELD_TIMER = NewTimer()
TimerStart(LUA_VAR_SPIRITWALKER_SHIELD_TIMER,dur,false,function()
DisableTrigger(SpiritWalker_Shield_Listener)
end)
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[13]) then
LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT = LUA_VAR_SPIRITWALKER_SHIELD_AMOUNT*1.1
local g = CreateGroup()
GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),600.0, Filter(function()
if (IsUnitAlly(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and UnitHasBuffBJ(GetFilterUnit(),FourCC('Bspl'))) then
DamagePackageRestoreHealth(GetFilterUnit(),10,true,nil,caster)
end
return false
end ) )
DestroyGroup(g)
end
end
local SpiritWalker_Binding_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local dur = 3.0
local dummyUnit = DamagePackageCreateDummy(GetOwningPlayer(caster), 'o01E', GetUnitX(caster), GetUnitY(caster), GetUnitFacing(caster), dur+0.21)
local dmg = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*1.75
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[14]) then
dur = dur + 2.0
dmg = dmg*1.3
end
local i = dur/0.2
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[3]) then
SetUnitMoveSpeed(dummyUnit,225.0)
end
SetUnitVertexColorBJ(dummyUnit, 45, 45, 100, 50)
TimerStart(NewTimer(),0.2,true,function()
i = i - 1
IssuePointOrder(dummyUnit,'move',GetUnitX(target),GetUnitY(target))
if (i <= 0 or not IsUnitAliveBJ(target)) then
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl',GetUnitX(dummyUnit),GetUnitY(dummyUnit)))
RemoveUnit(dummyUnit)
ReleaseTimer()
elseif (DistanceBetweenXY(GetUnitX(target),GetUnitY(target),GetUnitX(dummyUnit),GetUnitY(dummyUnit)) < 125.0) then
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[7]) then
dmg = dmg*(1 + ((math.abs(i - dur/0.2)*0.2)*0.15))
end
UnitDamageTargetBJ(caster,target,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl',GetUnitX(target),GetUnitY(target)))
DamagePackageDummyTarget(caster,'A06V','ensnare',target)
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[3]) then
DamagePackageDummyTarget(caster,'A029','bloodlust')
end
RemoveUnit(dummyUnit)
ReleaseTimer()
end
end)
end
local SpiritWalker_Vigor_f = function()
local caster = GetTriggerUnit()
local dmg = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*3.5
local radius = 1000.0
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[16]) then
dmg = dmg*1.3
radius = radius*1.5
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl',GetUnitX(caster),GetUnitY(caster)))
end
local g = CreateGroup()
GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),radius, Filter(function() return
IsUnitAlly(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) end ) )
GroupUtilsAction(g,function()
DamagePackageRestoreHealth(LUA_FILTERUNIT,dmg,false,
'Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl',caster)
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\Bolt\\BoltImpact.mdl',GetUnitX(LUA_FILTERUNIT),GetUnitY(LUA_FILTERUNIT)))
DamagePackageDummyTarget(LUA_VAR_SPIRITWALKER_UNIT,'A05E','bloodlust',LUA_FILTERUNIT)
DamagePackageDummyTarget(LUA_VAR_SPIRITWALKER_UNIT,'A06P','spiritlink',LUA_FILTERUNIT)
end)
DestroyGroup(g)
end
local SpiritWalker_Stomp_f = function()
local caster = GetTriggerUnit()
local dmg = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*2.5
local distance = 300.0
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[17]) then distance = distance + 100.0 end
DamagePackageDealAOE(caster, GetUnitX(caster), GetUnitY(caster),
300.0, dmg, false, false, '', '', 'Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl', 0.8)
local collisionFunc = function(id)
DamagePackageDummyTarget(LUA_KB_DATA[id].unit,'A04B','slow')
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl',GetUnitX(LUA_KB_DATA[id].unit),GetUnitY(LUA_KB_DATA[id].unit)))
if (LUA_VAR_SPIRITWALKER_ITEMBOOL[17]) then
DamagePackageDummyTarget(LUA_KB_DATA[id].unit,'A04O','thunderbolt')
UnitDamageTargetBJ(caster,LUA_KB_DATA[id].unit,dmg*0.5,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
end
BasicKnockbackAreaPoint(caster,GetUnitX(caster),GetUnitY(caster),true,300.0,distance,false,0.33,collisionFunc,nil,nil)
end
local cond = function() return
UnitHasBuffBJ(udg_DamageEventTarget,FourCC('Bspl'))
and IsUnitAlly(udg_DamageEventTarget,GetOwningPlayer(LUA_VAR_SPIRITWALKER_UNIT))
end
SpellPackCreateDamageTrigger(SpiritWalker_Shield_Listener,SpiritWalker_Shield_Listener_f,1,cond,"udg_DamageModifierEvent")
SpellPackCreateTrigger(SpiritWalker_Mend,SpiritWalker_Mend_f,pInt,'A06R')
SpellPackCreateTrigger(SpiritWalker_Shield,SpiritWalker_Shield_f,pInt,'A06Q')
SpellPackCreateTrigger(SpiritWalker_Binding,SpiritWalker_Binding_f,pInt,'A06S')
SpellPackCreateTrigger(SpiritWalker_Vigor,SpiritWalker_Vigor_f,pInt,'A06T',EVENT_PLAYER_UNIT_SPELL_FINISH)
SpellPackCreateTrigger(SpiritWalker_Stomp,SpiritWalker_Stomp_f,pInt,'A06U')
SpellPackCreateTrigger(SpiritWalker_Link,SpiritWalker_Link_f,pInt,'A06P')
DisableTrigger(SpiritWalker_Shield_Listener)
end
function HeadhunterInit(unit, i)
LUA_VAR_HEADHUNTER_UNIT = unit
LUA_VAR_HEADHUNTER_ITEMBOOL = {}
HeadhunterItemsInit(i)
HeadhunterAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNHeadhunter.blp'
LeaderboardUpdateHeroIcon(i)
end
function HeadhunterItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I09Y", -- 1 (Q) Absorption Stone
"I0A1", -- 2 (W) Jammin' Health Manual
"I0A4", -- 3 (E) Bear Trap
"I0A7", -- 4 (Trait) Hexing Elixir
"I09Z", -- 5 (Q) Feathered Javelin
"I0A2", -- 6 (W) Jungle Stompers
"I0A5", -- 7 (E) Vial of Fiery Voodoo
"I0A8", -- 8 (Trait) Trinket of Zandalar
"I0A0", -- 9 (Q) Explosive Tip
"I0A3", -- 10 (W) Bottled Mojo
"I0A6" -- 11 (E) Trap Launcher
}
local itemTableUlt = {
"I0A9", -- 12 (Q) Gonk's Piercing Talon
"I0AA", -- 13 (W) Akunda's Voltaic Grasp
"I0AB", -- 14 (E) Krag'wa's Hexing Maw
"I0AC", -- 15 (Trait) Bwonsamdi's Devilish Pact
"I0AD", -- 16 (R1) Jani's Poaching Treasure
"I0AE" -- 17 (R2) Shadra's Venomous Spline
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if itemId == FourCC("I0A6") then IncUnitAbilityLevel(LUA_VAR_HEADHUNTER_UNIT,FourCC("A06X"))
elseif itemId == FourCC("I0A3") then IncUnitAbilityLevel(LUA_VAR_HEADHUNTER_UNIT,FourCC("A06Y"))
elseif itemId == FourCC("I0A7") then
LUA_VAR_HEADHUNTER_ATK = 0
local func = function()
LUA_VAR_HEADHUNTER_ATK = LUA_VAR_HEADHUNTER_ATK + 1
if LUA_VAR_HEADHUNTER_ATK >= 3 then
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, udg_DamageEventSource, true)*0.50
UnitDamageTargetBJ(udg_DamageEventSource, udg_DamageEventTarget, dmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
DamagePackageDummyTarget(udg_DamageEventSource,'A074','hex',udg_DamageEventTarget)
LUA_VAR_HEADHUNTER_ATK = 0
end
end
trig = CreateTrigger()
local cond = function() return
udg_DamageEventSource == udg_playerHero[pInt]
and udg_DamageEventAttackT == udg_ATTACK_TYPE_HERO
and not udg_IsDamageSpell
end
SpellPackCreateDamageTrigger(trig,func,pInt,cond,"udg_DamageEvent")
elseif itemId == FourCC("I0AA") then
LUA_VAR_HEADHUNTER_ATK2 = 0
local func = function()
LUA_VAR_HEADHUNTER_ATK2 = LUA_VAR_HEADHUNTER_ATK2 + 1
if LUA_VAR_HEADHUNTER_ATK2 >= 4 then
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, udg_DamageEventSource, true)*1.25
UnitDamageTargetBJ(udg_DamageEventSource, udg_DamageEventTarget, dmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
DamagePackageDummyTarget(udg_DamageEventSource,'A04B','slow',udg_DamageEventTarget)
SpellPackLightningEffect(udg_DamageEventSource,nil,nil,udg_DamageEventTarget)
LUA_VAR_HEADHUNTER_ATK2 = 0
end
end
trig = CreateTrigger()
local cond = function() return
udg_DamageEventSource == udg_playerHero[pInt]
and udg_DamageEventAttackT == udg_ATTACK_TYPE_HERO
and not udg_IsDamageSpell
end
SpellPackCreateDamageTrigger(trig,func,pInt,cond,"udg_DamageEvent")
elseif itemId == FourCC("I0AC") then HeroAddStat(udg_playerHero[pInt], 1, LUA_VAR_HEADHUNTER_TROPHY_GAINED)
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl',
GetUnitX(udg_playerHero[pInt]), GetUnitY(udg_playerHero[pInt])))
LUA_VAR_HEADHUNTER_PACT_GAINED = 0.0 + LUA_VAR_HEADHUNTER_TROPHY_GAINED
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_HEADHUNTER_ITEMBOOL,itemTableUlt)
end
function HeadhunterAbilsInit(pInt)
Headhunter_Trophy = CreateTrigger()
Headhunter_Javelin = CreateTrigger()
Headhunter_Regen = CreateTrigger()
Headhunter_Trap = CreateTrigger()
Headhunter_Track = CreateTrigger()
Headhunter_Venom = CreateTrigger()
LUA_VAR_HEADHUNTER_TROPHY_COUNT = 0.0
LUA_VAR_HEADHUNTER_TROPHY_GAINED = 0.0
TriggerRegisterAnyUnitEventBJ(Headhunter_Trophy, EVENT_PLAYER_UNIT_DEATH)
TriggerAddCondition(Headhunter_Trophy, Filter( function() return
IsUnitEnemy(GetTriggerUnit(), GetOwningPlayer(udg_playerHero[pInt]))
and (GetOwningPlayer(GetTriggerUnit()) == Player(24) or IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO))
and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_ANCIENT)
and IsUnitAliveBJ(udg_playerHero[pInt])
and DistanceBetweenXY(GetUnitX(udg_playerHero[pInt]),GetUnitY(udg_playerHero[pInt]),
GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit())) <= 1000.0 end ) )
TriggerAddAction(Headhunter_Trophy, function()
if GetOwningPlayer(GetTriggerUnit()) == Player(24) and GetUnitLevel(GetTriggerUnit()) > 4 then
LUA_VAR_HEADHUNTER_TROPHY_COUNT = LUA_VAR_HEADHUNTER_TROPHY_COUNT + 0.25
elseif IsUnitType(GetTriggerUnit(),UNIT_TYPE_HERO) then
LUA_VAR_HEADHUNTER_TROPHY_COUNT = LUA_VAR_HEADHUNTER_TROPHY_COUNT + 0.50
end
if LUA_VAR_HEADHUNTER_TROPHY_COUNT >= 1.0 then
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Items\\AIam\\AIamTarget.mdl',
GetUnitX(udg_playerHero[pInt]), GetUnitY(udg_playerHero[pInt])))
HeroAddStat(udg_playerHero[pInt], 1, 1.0)
if LUA_VAR_HEADHUNTER_ITEMBOOL[8] then HeroAddStat(udg_playerHero[pInt], 0, 1.0) end -- Zandalar Trinket
LUA_VAR_HEADHUNTER_TROPHY_COUNT = LUA_VAR_HEADHUNTER_TROPHY_COUNT - 1.0
LUA_VAR_HEADHUNTER_TROPHY_GAINED = LUA_VAR_HEADHUNTER_TROPHY_GAINED + 1.0
if LUA_VAR_HEADHUNTER_ITEMBOOL[15] then -- Bwonsamdi
HeroAddStat(udg_playerHero[pInt], 1, 1.0)
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl',
GetUnitX(udg_playerHero[pInt]), GetUnitY(udg_playerHero[pInt])))
LUA_VAR_HEADHUNTER_PACT_GAINED = LUA_VAR_HEADHUNTER_PACT_GAINED + 1.0
if LUA_VAR_HEADHUNTER_PACT_TMR then ReleaseTimer(LUA_VAR_HEADHUNTER_PACT_TMR) end
LUA_VAR_HEADHUNTER_PACT_TMR = NewTimer()
TimerStart(LUA_VAR_HEADHUNTER_PACT_TMR,120.0,false,function()
HeroRemoveStat(udg_playerHero[pInt], 1, LUA_VAR_HEADHUNTER_PACT_GAINED)
LUA_VAR_HEADHUNTER_PACT_GAINED = 0.0
SpellPackInvalidMsg(caster,'Bwonsamdi is displeased.')
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl',
GetUnitX(udg_playerHero[pInt]), GetUnitY(udg_playerHero[pInt])))
end)
end
end
end)
local Headhunter_Javelin_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*2.0
local range = 1000.0
local strMis = 'abilities\\weapons\\huntermissile\\huntermissile.mdl'
local strDmg = 'Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl'
local pierceBool = true
if LUA_VAR_HEADHUNTER_ITEMBOOL[5] then dmg = dmg*1.1 range = range*1.1 end
if LUA_VAR_HEADHUNTER_ITEMBOOL[12] then pierceBool = false end
LUA_VAR_HEADHUNTER_JAV_EXP = false
local func = function(u, o)
if IsUnitType(u,UNIT_TYPE_HERO) and DistanceBetweenXY(casterX, casterY, GetUnitX(u), GetUnitY(u)) >= 500.0
and not IsUnitType(u,UNIT_TYPE_ANCIENT) then
BlzEndUnitAbilityCooldown(caster,FourCC('A032'))
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl',GetUnitX(caster), GetUnitY(caster)))
if LUA_VAR_HEADHUNTER_ITEMBOOL[1] then DamagePackageAddMana(caster,40.0,false) end
end
if LUA_VAR_HEADHUNTER_ITEMBOOL[9] and not LUA_VAR_HEADHUNTER_JAV_EXP and GetConvertedPlayerId(GetOwningPlayer(u)) > 10 then
LUA_VAR_HEADHUNTER_JAV_EXP = true -- only allow one explosion per use (when piercing)
DamagePackageDealAOE(caster, GetUnitX(u), GetUnitY(u), 275.0, dmg*0.50, false, false,
'', '', 'Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl', 0.75)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',GetUnitX(u), GetUnitY(u)))
end
end
LuaMissile.create(caster, tarX, tarY, range, 1.5, dmg, 65.0, pierceBool, false, false,nil,func,strMis,strDmg,60.0,1.0,nil,nil,nil)
end
local Headhunter_Regen_f = function()
local caster = GetTriggerUnit()
local healing = 15.0 -- %
local dur = 6.0
local g = CreateGroup()
GroupEnumUnitsInRange(g, GetUnitX(caster), GetUnitY(caster), 1600.0, Condition(function() return
IsUnitAliveBJ(GetFilterUnit())
and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) end ))
local size = BlzGroupGetSize(g) or 0
if size < 1 then
healing = healing*2.0
if LUA_VAR_HEADHUNTER_ITEMBOOL[2] then DamagePackageAddMana(caster,25,false) end
end
healing = healing/dur
DestroyGroup(g)
TimerStart(NewTimer(),1.0,true,function()
dur = dur - 1
DamagePackageRestoreHealth(caster, healing, true, 'Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl',caster)
if dur <= 0 then ReleaseTimer() end
end)
if LUA_VAR_HEADHUNTER_ITEMBOOL[6] then DamagePackageDummyTarget(caster,'A05E','bloodlust') end
end
local Headhunter_Trap_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
if not IsUnitType(caster,UNIT_TYPE_HERO) then caster = udg_playerHero[pInt] end -- since traps can be placed by dummies
local dur = 90.0
local armTime = 3.0
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*2.25
local trap = DamagePackageCreateDummy(GetOwningPlayer(caster),'e00U', tarX, tarY, 270.0, dur)
local trig = CreateTrigger()
if LUA_VAR_HEADHUNTER_ITEMBOOL[11] then armTime = armTime - 1.5 end
if LUA_VAR_HEADHUNTER_ITEMBOOL[3] then dur = dur + 30 dmg = dmg*1.20 end
SetUnitVertexColorBJ(trap, 100, 10, 10, 60)
TimerStart(NewTimer(),armTime,false,function()
SetUnitVertexColorBJ(trap, 100, 100, 100, 0)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',GetUnitX(trap), GetUnitY(trap)))
TriggerRegisterUnitInRangeSimple( trig, 200.0, trap )
TriggerAddCondition(trig, Filter( function() return
(IsUnitEnemy(GetTriggerUnit(),GetOwningPlayer(caster)) or GetOwningPlayer(GetTriggerUnit()) == Player(24))
and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_DEAD)
and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_MAGIC_IMMUNE)
and not IsUnitType(GetTriggerUnit(),UNIT_TYPE_ANCIENT) end ) )
local tmr = NewTimer()
TimerStart(tmr,dur,false,function() if trig then DestroyTrigger(trig) end ReleaseTimer() end)
TriggerAddAction(trig, function()
if IsUnitAliveBJ(trap) then
local u = GetTriggerUnit()
if GetOwningPlayer(GetTriggerUnit()) == Player(24) then
dmg = dmg *1.25
end
TimerStart(NewTimer(),3.0,false,function() DamagePackageDummyTarget(caster,'A073','faeriefire',u) ReleaseTimer() end)
if LUA_VAR_HEADHUNTER_ITEMBOOL[7] then
DamagePackageDealAOE(caster, GetUnitX(trap), GetUnitY(trap), 250.0, dmg, false, false,
'', '', 'Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl', 0.75)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',GetUnitX(trap), GetUnitY(trap)))
else
UnitDamageTargetBJ(caster,u,dmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
end
if LUA_VAR_HEADHUNTER_ITEMBOOL[14] then -- Krag'wa
DamagePackageDummyTarget(caster,'A076','hex',u)
else -- default root
DamagePackageDummyTarget(caster,'A072','ensnare',u)
end
ReleaseTimer(tmr)
if IsUnitType(u,UNIT_TYPE_HERO) then
DisplayTimedTextToPlayer(GetOwningPlayer(caster), 0, 0, 2.0,
'|cffffc800Trap activated by|r ' .. GetPlayerName(GetOwningPlayer(u)) .. '|cffffc800!|r')
PingMinimapForPlayer(GetOwningPlayer(caster), GetUnitX(trap), GetUnitY(trap), 3.0)
end
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl',GetUnitX(u), GetUnitY(u)))
KillUnit(trap)
end
DestroyTrigger(GetTriggeringTrigger())
end)
ReleaseTimer()
end)
end
local Headhunter_Track_f = function()
local caster, casterX, casterY = SpellPackSetupInstantAbil()
local g = CreateGroup()
local destroyDur = 6.0
local p = GetOwningPlayer(caster)
GroupEnumUnitsInRange(g, GetUnitX(caster), GetUnitY(caster), 1600.0, Condition(function() return
IsUnitAliveBJ(GetFilterUnit())
and IsUnitEnemy(GetFilterUnit(),p)
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) end ))
local size = BlzGroupGetSize(g) or 0
if size > 0 then
local trackUnit = BlzGroupUnitAt(g,0)
GroupUtilsAction(g,function()
if GetUnitLifePercent(LUA_FILTERUNIT) < GetUnitLifePercent(trackUnit) then
trackUnit = LUA_FILTERUNIT
end
if LUA_VAR_HEADHUNTER_ITEMBOOL[16] then -- Jani
DamagePackageDummyAoE(caster, 'A06X', 'stasistrap', nil, GetUnitX(LUA_FILTERUNIT), GetUnitY(LUA_FILTERUNIT),
GetUnitX(LUA_FILTERUNIT), GetUnitY(LUA_FILTERUNIT), nil)
end
end)
DamagePackageDummyTarget(caster,'A05G','bloodlust')
DisplayTimedTextToForce(GetPlayersAll(), 2.0,
GetPlayerName(p) .. ' |cffffc800is tracking|r ' .. GetPlayerName(GetOwningPlayer(trackUnit)))
PingMinimapForPlayer(p, GetUnitX(trackUnit), GetUnitY(trackUnit), 3.0)
CreateMinimapIconOnUnitBJ(trackUnit, 255, 255, 90, 'UI\\Minimap\\Minimap-QuestObjectiveBonus.mdl', FOG_OF_WAR_MASKED)
local icon = GetLastCreatedMinimapIcon()
SetMinimapIconOrphanDestroy( icon, true )
UnitShareVisionBJ( true, trackUnit, p )
DamagePackageDummyTarget(caster,'A073','faeriefire',trackUnit)
if GetUnitLifePercent(trackUnit) < 50.0 then
destroyDur = destroyDur + 6.0
TimerStart(NewTimer(),5.9,false,function()
if IsUnitAliveBJ(caster) and IsUnitAliveBJ(trackUnit) then
DamagePackageDummyTarget(caster,'A05G','bloodlust')
DamagePackageDummyTarget(caster,'A073','faeriefire',trackUnit)
else
UnitShareVisionBJ( false, trackUnit, p )
end
ReleaseTimer()
end)
end
TimerStart(NewTimer(),destroyDur,false,function()
if icon then
DestroyMinimapIcon(icon)
end
UnitShareVisionBJ( false, trackUnit, p )
ReleaseTimer()
end)
else
SpellPackInvalidMsg(caster,'There were no nearby targets to track!')
end
DestroyGroup(g)
end
local Headhunter_Venom_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*2.0
local strMis = 'abilities\\weapons\\wyvernspear\\wyvernspearmissile.mdl'
local strDmg = 'Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl'
local func = function(u, o)
if GetUnitLifePercent(u) < 50.0 then
DamagePackageDummyTarget(caster,'A009','slow',u)
UnitDamageTargetBJ(caster,u,dmg*0.50, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\AcidBomb\\BottleMissile.mdl',GetUnitX(u), GetUnitY(u)))
if LUA_VAR_HEADHUNTER_ITEMBOOL[17] then
DamagePackageDealOverTime(caster, u, dmg/6.0, 6.0, 'Abilities\\Spells\\Other\\AcidBomb\\BottleImpact.mdl')
end
end
end
LuaMissile.create(caster, tarX, tarY, 1000.0, 1.5, dmg, 75.0, true, false, false,nil,func,strMis,strDmg,60.0,1.0,nil,nil,nil)
end
SpellPackCreateTrigger(Headhunter_Javelin,Headhunter_Javelin_f,pInt,'A032')
SpellPackCreateTrigger(Headhunter_Regen,Headhunter_Regen_f,pInt,'A06Y')
SpellPackCreateTrigger(Headhunter_Trap,Headhunter_Trap_f,pInt,'A06X')
SpellPackCreateTrigger(Headhunter_Track,Headhunter_Track_f,pInt,'A06Z')
SpellPackCreateTrigger(Headhunter_Venom,Headhunter_Venom_f,pInt,'A070')
end
function CryptFiendInit(unit, i)
CryptFiendItemsInit(i)
HeroTriggersCryptFiend(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNCryptFiend.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Crypt_Fiend_Weave )
EnableTrigger( gg_trg_Crypt_Fiend_Silk_Surge )
EnableTrigger( gg_trg_Crypt_Fiend_Cocoon )
EnableTrigger( gg_trg_Crypt_Fiend_Burrow )
EnableTrigger( gg_trg_Crypt_Fiend_Might_of_Nerub )
EnableTrigger( gg_trg_Crypt_Fiend_Nerub )
udg_cryptFiendUnit = unit
--DisplayTextToForce( GetPlayersAll(), "Debug: Crypt Fiend Initialized")
end
function CryptFiendItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I00R", -- 1 Nerubian Threads (Q)
"I00S", -- 2 Arachnid Ring of Detonation (W)
"I00T", -- 3 Regenerative Fibers (E)
"I00U", -- 4 Burrowing Claws (F)
"I00V", -- 5 Wand of the Fallen Kingdom (Q)
"I03X", -- 6 Arachnid Ring of Binding (W)
"I043", -- 7 Corrosive Fibers (E)
"I044", -- 8 Spawning Sac (F)
"I03W", -- 9 Scroll of Ahn'Qiraj (Q)
"I03Y", -- 10 Arachnid Ring of Corrosion (W)
"I045" -- 11 Reverberating Fibers (E)
}
local itemTableUlt = {
"I041", -- 12 Mantle of Yath'amon (Q)
"I040", -- 13 Ix'lar's Noxious Gland (W)
"I042", -- 14 Anub'esset's Husk (E)
"I03Z", -- 15 Voru'kar's Talon (F)
"I047", -- 16 Shell of Anub'arak (R1)
"I046", -- 17 Crown of Anub'Rekhan (R2)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00U")) then IncUnitAbilityLevel(udg_cryptFiendUnit,FourCC("A024")) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_cryptFiendItemBool,itemTableUlt)
end
function HeroTriggersCryptFiend(pInt)
CryptFiend_Webfield = CreateTrigger()
local CryptFiend_Webfield_f = function()
local caster = GetTriggerUnit()
local x1 = GetSpellTargetX()
local y1 = GetSpellTargetY()
local p = GetOwningPlayer(caster)
local x2
local y2
local u
u = CreateUnit(p, FourCC('e00F'), x1, y1, 270.0) -- create center web
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
if (udg_cryptFiendItemBool[16]) then
u = CreateUnit(p, FourCC('u00D'), x1, y1, 270.0) -- create mender if item held
UnitApplyTimedLifeBJ( 15.0, FourCC('B01D'), u )
for i = 1,6 do -- create radius eggs w/ ult item
x2,y2 = PolarProjectionXY(x1,y1,165.0,i*60)
u = CreateUnit(p, FourCC('e00F'), x2, y2, 270.0)
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
end
else
for i = 1,4 do -- create radius eggs w/o ult item
x2,y2 = PolarProjectionXY(x1,y1,165.0,i*90-45)
u = CreateUnit(p, FourCC('e00F'), x2, y2, 270.0)
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
end
end
u = nil
end
TriggerRegisterPlayerUnitEvent(CryptFiend_Webfield,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(CryptFiend_Webfield, Filter( function() return GetSpellAbilityId() == FourCC('A04W') end ) )
TriggerAddAction(CryptFiend_Webfield,CryptFiend_Webfield_f)
end
function GargoyleInit(unit, i)
GargoyleItemsInit(i)
GargoyleAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNGargoyle.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Gargoyle_Blade )
EnableTrigger( gg_trg_Gargoyle_Clutch )
EnableTrigger( gg_trg_Gargoyle_Stone_Form )
EnableTrigger( gg_trg_Gargoyle_Ancient_Statue )
EnableTrigger( gg_trg_Gargoyle_Vampiric_Claws )
udg_gargoyleUnit = unit
--DisplayTextToForce( GetPlayersAll(), "Debug: Gargoyle Initialized")
end
function GargoyleItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I00W", -- 1 Necrotic Orb (Q)
"I00X", -- 2 Preying Claws (W)
"I00Y", -- 3 Hardened Carapace (E)
"I00Z", -- 4 Vampiric Maw (Trait)
"I010", -- 5 Caustic Orb (Q)
"I048", -- 6 Deathgrip Appendage (W)
"I04B", -- 7 Spire Membrane (E)
"I04C", -- 8 Mana-Eater Fangs (Trait)
"I04A", -- 9 Freezing Orb (Q)
"I049", -- 10 Lunging Wings (W)
"I04E" -- 11 Dimensional Rind (E)
}
local itemTableUlt = {
"I04D", -- 12 Maleficent Orb (Q)
"I04F", -- 13 Devilish Talons (W)
"I04G", -- 14 Indomitable Husk (E)
"I04H", -- 15 Impish Pact (Stat Passive)
"I04J", -- 16 Bottle of Echoing Terror (R1)
"I04I", -- 17 Unholy Monster Garb (R2)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00Y")) then IncUnitAbilityLevel(udg_gargoyleUnit,FourCC("A043"))
elseif (itemId == FourCC("I04D")) then IncUnitAbilityLevel(udg_gargoyleUnit,FourCC("A00T"))
elseif (itemId == FourCC("I04F")) then IncUnitAbilityLevel(udg_gargoyleUnit,FourCC("A00S"))
elseif (itemId == FourCC("I04H")) then HeroAddStatTimer(udg_gargoyleUnit, 1, 1, 45.0, 15) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_gargoyleItemBool,itemTableUlt)
end
function GargoyleAbilsInit(pInt)
Gargoyle_Unholy_Screech = CreateTrigger()
local Gargoyle_Unholy_Screech_f = function()
local caster = GetTriggerUnit()
local x = GetUnitX(caster)
local y = GetUnitY(caster)
DamagePackageDummyAoE(caster, 'A051', 'silence', nil, x, y, x, y, nil)
if (udg_gargoyleItemBool[16]) then
LUA_VAR_G_US_EFFECT = AddSpecialEffect('Abilities\\Weapons\\DragonHawkMissile\\DragonHawkMissile.mdl',x,y)
TimerStart(NewTimer(),3.0,false,function()
DamagePackageDummyAoE(caster, 'A051', 'silence', nil, x, y, x, y, nil)
DestroyEffect(LUA_VAR_G_US_EFFECT)
LUA_VAR_G_US_EFFECT = nil
ReleaseTimer()
end)
end
end
TriggerRegisterPlayerUnitEvent(Gargoyle_Unholy_Screech,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Gargoyle_Unholy_Screech, Filter( function() return GetSpellAbilityId() == FourCC('A050') end ) )
TriggerAddAction(Gargoyle_Unholy_Screech,Gargoyle_Unholy_Screech_f)
end
function AcolyteInit(unit, i)
AcolyteItemsInit(i)
AcolyteAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNAcolyte.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Acolyte_Blight_Tumor_Activate )
EnableTrigger( gg_trg_Acolyte_Blight_Structure_Destroyed )
EnableTrigger( gg_trg_Acolyte_Ritual )
EnableTrigger( gg_trg_Acolyte_Desecrate )
EnableTrigger( gg_trg_Acolyte_Citadel )
EnableTrigger( gg_trg_Acolyte_Unit_Trained)
udg_acolyteUnit = unit
--DisplayTextToForce( GetPlayersAll(), "Debug: Acolyte Initialized")
end
function AcolyteItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I011", -- 1 Summoning Stone (B)
"I012", -- 2 Dagger of Spite (W)
"I013", -- 3 Corked Nightmares (E)
"I014", -- 4 Rod of Necromancy (Active)
"I016", -- 5 Deathly Sneakers (Passive)
"I04P", -- 6 Corrosive Dagger Hilt (W)
"I04L", -- 7 Book of Reanimation (E)
"I04O", -- 8 Scourge Knell (B,Item)
"I04S", -- 9 Potion of Undeath (B)
"I04Q", -- 10 Chipped Skull (W)
"I04M" -- 11 Book of Noxious Death (E)
}
local itemTableUlt = {
"I015", -- 12 Nerubian Hive Crystal (B)
"I04T", -- 13 Hood of the Shadow Council (W)
"I04N", -- 14 Book of Grasping Nightmares (E)
"I04U", -- 15 Scourge Invasion Portal (Trait)
"I04K", -- 16 Unholy Juggernaut Plating (R1)
"I04R", -- 17 Crown of the Lich King (R2)
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC('I015')) then
if (IsUnitAlly(udg_acolyteUnit,Player(12))) then -- unlock research for faction owner
SetPlayerTechResearchedSwap( FourCC('R009'), 1, Player(12) ) -- horde
else
SetPlayerTechResearchedSwap( FourCC('R009'), 1, Player(13) ) -- alliance
end
elseif (itemId == FourCC('I04U')) then
TimerStart(NewTimer(),18.0,true,function()
SetPlayerState(GetOwningPlayer(udg_acolyteUnit), PLAYER_STATE_RESOURCE_LUMBER,
GetPlayerState(GetOwningPlayer(udg_acolyteUnit),PLAYER_STATE_RESOURCE_LUMBER) + 1)
end)
elseif (itemId == FourCC("I04T")) then
IncUnitAbilityLevel(udg_acolyteUnit,FourCC("A01F"))
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_acolyteItemBool,itemTableUlt)
end
function AcolyteAbilsInit(pInt)
Acolyte_Transform = CreateTrigger()
local Acolyte_Transform_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local id = GetUnitTypeId(target)
if (id == FourCC('u007') or id == FourCC('u008') or id == FourCC('u009') or id == FourCC('u00A') or id == FourCC('u00B')) then -- eligible minions
local hp = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*5.5
local atk = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*1.65
local x = GetUnitX(target)
local y = GetUnitY(target)
SetUnitExploded(target,true)
KillUnit(target)
local u = CreateUnit(GetOwningPlayer(caster), FourCC('u00E'), x, y, 270.0)
local mhp = BlzGetUnitMaxHP(u)
DestroyEffect( AddSpecialEffect('Units\\Undead\\Abomination\\AbominationExplosion.mdl',x,y))
DestroyEffect( AddSpecialEffect('Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl',x,y))
if (udg_acolyteItemBool[16]) then
BlzSetUnitBaseDamage( u, math.floor(atk*1.25), 0 )
BlzSetUnitMaxHP( u, math.floor(mhp + hp*1.75) )
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
else
BlzSetUnitBaseDamage( u, math.floor(atk), 0 )
BlzSetUnitMaxHP( u, math.floor(mhp + hp) )
UnitApplyTimedLifeBJ( 24.0, FourCC('BTLF'), u )
end
SetUnitLifePercentBJ(u, 100.0)
u = nil
else
TimerStart(NewTimer(),0.33,false,function()
BlzEndUnitAbilityCooldown( udg_acolyteUnit, FourCC('A044') )
ReleaseTimer()
end)
DisplayTimedTextToPlayer(GetOwningPlayer(caster),0,0,1.5,'|cffffc800Invalid target, choose an undead minion!|r')
end
caster = nil
target = nil
end
TriggerRegisterPlayerUnitEvent(Acolyte_Transform,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Acolyte_Transform, Filter( function() return GetSpellAbilityId() == FourCC('A044') end ) )
TriggerAddAction(Acolyte_Transform,Acolyte_Transform_f)
end
function KnightInit(unit, i)
KnightItemsInit(i)
KnightAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNKnight.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Knight_Joust )
EnableTrigger( gg_trg_Knight_Blood )
EnableTrigger( gg_trg_Knight_Voidwalker )
EnableTrigger( gg_trg_Knight_Conch )
EnableTrigger( gg_trg_Knight_Hunter )
udg_knightUnit = unit
udg_heroCanRegenMana[i] = true
--DisplayTextToForce( GetPlayersAll(), "Debug: Knight Initialized")
end
function KnightItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I017", -- 1 Explosive Lance (Q)
"I018", -- 2 Inoculated Blood (W)
"I019", -- 3 Echoing Scripture (E)
"I01A", -- 4 Tavern Leftovers (Trait)
"I01B", -- 5 Sharpening Stone (Q)
"I04V", -- 6 Alacrity Admixture (W)
"I04W", -- 7 Glowing Witcher Bounty (E)
"I04X", -- 8 Tavern Bounty Board (Trait)
"I04Z", -- 9 Reinforced Lance Stock (Q)
"I054", -- 10 (W) Flask of Arcana
"I055" -- 11 (Active) Sightly Ward
}
local itemTableUlt = {
"I050", -- 12 (Q) Helm of the Bandit Lord
"I052", -- 13 (Passive) Curiass of the Dragon Lord
"I051", -- 14 (E) Mantle of the Void Lord
"I053", -- 15 (Trait) Deed of the Tavern Lord
"I056", -- 16 (R1) Club of the Magnataur Lord
"I04Y" -- 17 (R2) Buccaneer Cap of the Pirate Lord
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I04W")) then IncUnitAbilityLevel(udg_knightUnit,FourCC("A038"))
elseif (itemId == FourCC("I050")) then IncUnitAbilityLevel(udg_knightUnit,FourCC("A03A"))
elseif (itemId == FourCC("I052")) then HeroAddStatTimer(udg_knightUnit, 0, 1, 45.0, 12) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_knightItemBool,itemTableUlt)
end
function KnightAbilsInit(pInt)
Knight_Tusk = CreateTrigger()
local Knight_Tusk_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local damage = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*1.75
local pInt = GetConvertedPlayerId(GetOwningPlayer(target))
if (pInt > 14) then
damage = damage*2.0
end
UnitDamageTargetBJ(caster, target, damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
if (udg_knightItemBool[16] and pInt > 14 and GetWidgetLife(target) <= .405) then -- reset CD item true
PolledWait(0.20) -- hacky fix to wait for spell effect to register completely
BlzEndUnitAbilityCooldown(caster,FourCC('A058'))
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\NightElf\\Taunt\\TauntCaster.mdl',GetUnitX(caster),GetUnitY(caster)))
end
caster = nil
target = nil
end
TriggerRegisterPlayerUnitEvent(Knight_Tusk,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Knight_Tusk, Filter( function() return GetSpellAbilityId() == FourCC('A058') end ) )
TriggerAddAction(Knight_Tusk,Knight_Tusk_f)
end
function SpellBreakerInit(unit, i)
SpellBreakerItemsInit(i)
SpellBreakerAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNSpellBreaker.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Spell_Breaker_Absorb )
EnableTrigger( gg_trg_Spell_Breaker_Reflect )
EnableTrigger( gg_trg_Spell_Breaker_Aura )
EnableTrigger( gg_trg_Spell_Breaker_Shatter )
EnableTrigger( gg_trg_Spell_Breaker_Reduction )
udg_spellBreakerUnit = unit
udg_heroCanRegenMana[i] = true
--DisplayTextToForce( GetPlayersAll(), "Debug: Spell Breaker Initialized")
end
function SpellBreakerItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I01F", -- 1 (Q) Vexing Sphere
"I01G", -- 2 (W) Revitalizing Mana Crystal
"I01H", -- 3 (E) Overcharged Prism
"I01I", -- 4 (Runes) Mage Hunter Garb
"I01J", -- 5 (Q) Alacritous Bauble
"I057", -- 6 (W) Swordbreaker Shield
"I058", -- 7 (E) Empty Absorption Vial
"I059", -- 8 (Runes) Mana Shackles
"I05A", -- 9 (Q) Acceleration Sphere
"I05D", -- 10 (W) Manabreaker Shield
"I05E" -- 11 (Runes) Mage Hunter Boots
}
local itemTableUlt = {
"I05F", -- 12 (Q) Magnetic Sphere
"I05G", -- 13 (W) Crown of the Anti-Mage
"I05H", -- 14 (E) Thalassian Gauntlets
"I05I", -- 15 (Active) Mage Hunter Dagger
"I05C", -- 16 (R1) Quel'dorei Containment Pen
"I05B" -- 17 (R2) Hymn of the Quel'dorei
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I01I")) then udg_spellBreakerMaxRuneDur = 7.0
elseif (itemId == FourCC("I05A")) then IncUnitAbilityLevel(udg_spellBreakerUnit,FourCC("A02G"))
elseif (itemId == FourCC("I05G")) then IncUnitAbilityLevel(udg_spellBreakerUnit,FourCC("A02H")) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_spellBreakerItemBool,itemTableUlt)
end
function SpellBreakerAbilsInit(pInt)
SpellBreaker_Prison = CreateTrigger()
local SpellBreaker_Prison_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local pStat = BlzGetUnitIntegerField(target, UNIT_IF_PRIMARY_ATTRIBUTE)
local dur = 3.0
local ticks = 12
local abilId
local i = 0
local effect1
local effect2
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*.25
local x = GetUnitX(target)
local y = GetUnitY(target)
local u = CreateUnit(GetOwningPlayer(caster),FourCC('e00P'),x,y,270.0)
if (pStat == 2) then -- if intelligence, increase duration
dur = 4.0
abilId = 'A05B'
ticks = 16
effect = AddSpecialEffectTarget('Abilities\\Spells\\Other\\Drain\\ManaDrainCaster.mdl',target,'overhead')
else
abilId = 'A05A'
effect = AddSpecialEffectTarget('Abilities\\Spells\\Other\\Drain\\DrainCaster.mdl',target,'overhead')
end
UnitApplyTimedLifeBJ( dur, FourCC('BTLF'), u )
DamagePackageDummyAoE(udg_spellBreakerUnit, abilId, 'silence', nil, GetUnitX(target), GetUnitY(target), GetUnitX(target), GetUnitY(target), nil)
TimerStart(NewTimer(),0.25,true,function()
if (i >= ticks or not IsUnitAliveBJ(u) or not IsUnitAliveBJ(target)) then
DestroyEffect(effect)
KillUnit(u)
ReleaseTimer()
else
if (not IsUnitInRangeXY(target,x,y,300.0)) then
SetUnitX(target,x)
SetUnitY(target,y)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl',x,y))
end
if (udg_spellBreakerItemBool[16]) then
DamagePackageDealDirect(udg_spellBreakerUnit, target, dmg, 0, 0, false, 'Abilities\\Weapons\\BlackKeeperMissile\\BlackKeeperMissile.mdl')
end
i = i + 1
end
end)
end
TriggerRegisterPlayerUnitEvent(SpellBreaker_Prison,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(SpellBreaker_Prison, Filter( function() return GetSpellAbilityId() == FourCC('A059') end ) )
TriggerAddAction(SpellBreaker_Prison,SpellBreaker_Prison_f)
end
function PriestInit(unit, i)
PriestItemsInit(i)
PriestAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNPriest.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Priest_Heal )
EnableTrigger( gg_trg_Priest_Corrupt_Mind )
EnableTrigger( gg_trg_Priest_Shadow_Swap )
EnableTrigger( gg_trg_Priest_Mana_Reset )
EnableTrigger( gg_trg_Priest_Shadow_Form )
EnableTrigger( gg_trg_Priest_Visions )
udg_priestUnit = unit
udg_heroCanRegenMana[i] = false
LUA_TRG_PRIEST_MAX_MANA = ItemShopSetManaCap(i, 100)
end
function PriestItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I01K", -- 1 (Q) Lightweight Triage Manual
"I01L", -- 2 (W) Nightmare Catcher
"I01M", -- 3 (Mana) Irradiated Arcana
"I01N", -- 4 (F) Cultist Attire
"I01O", -- 5 (Q) Clutch Healing Salve
"I05J", -- 6 (W) Spiteful Visions
"I05L", -- 7 (E) Glyph of Infusion
"I05M", -- 8 (Trait) Orb of Dubious Dealings
"I05N", -- 9 (Q) Gem of Warding
"I05K", -- 10 (W) Phantasm Charm
"I05O" -- 11 (E) Pendant of Atonement
}
local itemTableUlt = {
"I05Q", -- 12 (Q) Veteran's Triage Satchel
"I05R", -- 13 (W) Doomsday Pact
"I05S", -- 14 (E) Scroll of Restitution
"I05T", -- 15 (Passive) Libram of Ancient Mana
"I05U", -- 16 (R1) Libram of Holy Fire
"I05P" -- 17 (R2) Libram of Shadows
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I01L")) then
IncUnitAbilityLevel(udg_priestUnit,FourCC("A017"))
elseif (itemId == FourCC("I01M")) then
DestroyTrigger(LUA_TRG_PRIEST_MAX_MANA)
LUA_TRG_PRIEST_MAX_MANA = ItemShopSetManaCap(GetConvertedPlayerId(GetOwningPlayer(udg_priestUnit)), 120)
elseif (itemId == FourCC("I01N")) then
IncUnitAbilityLevel(udg_priestUnit,FourCC("A01A"))
elseif (itemId == FourCC("I05T")) then
TimerStart(NewTimer(),4.0,true,function() DamagePackageAddMana(udg_priestUnit,1,true) end)
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_priestItemBool,itemTableUlt)
end
function PriestAbilsInit(pInt)
Priest_HolyFire = CreateTrigger()
local Priest_HolyFire_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local g = CreateGroup()
local x = GetUnitX(target)
local y = GetUnitY(target)
if (udg_priestItemBool[16]) then
DamagePackageRestoreHealth(target,50,true,nil,caster)
else
DamagePackageRestoreHealth(target,25,true,nil,caster)
end
GroupEnumUnitsInRange(g, x, y, 500.0, Condition( function() return
IsUnitAlly(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
local u = FirstOfGroup(g)
local i = 0
repeat
DamagePackageDummyTarget(u,'A05L','innerfire')
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\SpiritLink\\SpiritLinkZapTarget.mdl',GetUnitX(u),GetUnitY(u)))
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
i = i + 1
until (u == nil or i == 10) -- prevent infinite loop
end
DestroyGroup(g)
end
TriggerRegisterPlayerUnitEvent(Priest_HolyFire,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Priest_HolyFire, Filter( function() return GetSpellAbilityId() == FourCC('A05K') end ) )
TriggerAddAction(Priest_HolyFire,Priest_HolyFire_f)
end
function FootmanInit(unit, i)
FootmanItemsInit(i)
FootmanAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNFootman.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Footman_Shield_Bash )
EnableTrigger( gg_trg_Footman_Charge )
EnableTrigger( gg_trg_Footman_War_Banner )
udg_footmanChargeHashtable = InitHashtable()
udg_heroCanRegenMana[i] = false
udg_footmanUnit = unit
LUA_TRG_FOOTMAN_MAX_MANA = ItemShopSetManaCap(i, 100)
end
function FootmanItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I01P", -- 1 (Q) Shield of Malice
"I01Q", -- 2 (W) Gnomish Barrier Generator
"I01R", -- 3 (E) Deafening Blastcannon
"I01S", -- 4 (Trait) Socketed Belt
"I01T", -- 5 (Q) Regenerative Dragonmail
"I05V", -- 6 (W) Gnomish Acceleration Device
"I05W", -- 7 (E) Studded Assault Armor
"I05X", -- 8 (Trait) Replenishment Flask
"I05Y", -- 9 (Q) Spiked Shield Cover
"I05Z", -- 10 (W) Gnomish Inverted Magnet
"I060" -- 11 (E) Studded Assault Boots
}
local itemTableUlt = {
"I061", -- 12 (Q) Garth's Dragonfire Enchantment
"I062", -- 13 (W) Garth's Trinket of Defiance
"I063", -- 14 (E) Garth's Gauntlets of Heroism
"I064", -- 15 (Trait) Garth's Medallion of Courage
"I066", -- 16 (R1) Horn of the Fallen King
"I065" -- 17 (R2) Banner of Stormwind
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I060")) then IncUnitAbilityLevel(udg_footmanUnit,FourCC("A003"))
elseif (itemId == FourCC("I064")) then HeroAddStatTimer(udg_footmanUnit, 0, 1, 45.0, 15)
elseif (itemId == FourCC("I063")) then LUA_VAR_FM_INT = 0 end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_footmanItemBool,itemTableUlt)
end
function FootmanAbilsInit(pInt)
Footman_Pain = CreateTrigger()
Footman_Pain_Listener = CreateTrigger()
FootmanItem14_f = function() -- 14 (E) Garth's Gauntlets of Heroism
if (LUA_VAR_FM_INT == 1) then -- on 2nd use, do nothing, reset state, allow CD
LUA_VAR_FM_INT = 0
else -- on first use, refund mana cost and reset CD
LUA_VAR_FM_INT = 1
FootmanItem14_f2()
TimerStart(NewTimer(),5.0,false,function() -- if not used a 2nd time within CD period, reset the use loop
if (LUA_VAR_FM_INT == 1) then
LUA_VAR_FM_INT = 0
end
ReleaseTimer()
end)
end
end
FootmanItem14_f2 = function() -- reset the cd of heroic charge
DamagePackageAddMana(udg_footmanUnit, 25, false)
DestroyEffect( AddSpecialEffect( 'Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl', GetUnitX(udg_footmanUnit), GetUnitY(udg_footmanUnit) ) )
TimerStart(NewTimer(),0.21,false,function()
BlzEndUnitAbilityCooldown(udg_footmanUnit,FourCC('A003'))
ReleaseTimer()
end)
end
local Footman_Pain_f = function()
local dur = 6.0
if (udg_footmanItemBool[16]) then
dur = dur + 2.0
end
EnableTrigger(Footman_Pain_Listener)
TimerStart(NewTimer(),dur,false,function() DisableTrigger(Footman_Pain_Listener) ReleaseTimer() end)
end
TriggerRegisterPlayerUnitEvent(Footman_Pain,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Footman_Pain, Filter( function() return GetSpellAbilityId() == FourCC('A05P') end ) )
TriggerAddAction(Footman_Pain,Footman_Pain_f)
TriggerRegisterVariableEvent( Footman_Pain_Listener, "udg_DamageModifierEvent", EQUAL, 1.00 )
TriggerAddCondition(Footman_Pain_Listener, Filter( function() return
udg_DamageEventTarget == udg_footmanUnit
and not udg_IsDamageSpell
end ) )
TriggerAddAction(Footman_Pain_Listener, function()
local pInt = GetConvertedPlayerId(GetOwningPlayer(udg_DamageEventSource))
if (pInt > 12) then
udg_DamageEventAmount = udg_DamageEventAmount*.25
else
udg_DamageEventAmount = udg_DamageEventAmount*.50
end
end)
DisableTrigger(Footman_Pain_Listener)
end
function RiflemanInit(unit, i)
Rifleman_Data = {}
LUA_VAR_RIFLEMAN_UNIT = unit
LUA_VAR_RIFLEMAN_ITEMBOOL = {}
RiflemanItemsInit(i)
RiflemanAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNRifleman.blp'
LeaderboardUpdateHeroIcon(i)
end
function RiflemanItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I0AF", -- 1 (Q) Boomerang Sling
"I0AI", -- 2 (W) Dragonfire Barrel
"I0AL", -- 3 (E) Gnomish Rocket Gloves
"I0AO", -- 4 (Trait) Twin Barreled Flare Gun
"I0AG", -- 5 (Q) Unstable Gunpowder
"I0AJ", -- 6 (W) Binding Shells
"I0AM", -- 7 (E) Scout Uniform
"I0AP", -- 8 (Trait) Hunting Scope
"I0AH", -- 9 (Q) Long Rifle
"I0AK", -- 10 (W) Bouncing Vial
"I0AN" -- 11 (E) Rationing Kit
}
local itemTableUlt = {
"I0AQ", -- 12 (Q) Doomsday Cannon
"I0AR", -- 13 (W) Doomsday Shells
"I0AS", -- 14 (E) Inferno Treads
"I0AT", -- 15 (Trait) Gnomish Autoloader
"I0AU", -- 16 (R1) Squadron 88 Decal
"I0AV" -- 17 (R2) Rocket Magnet
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC('I0AR')) then
Rifleman_Data.wCharges.uses = Rifleman_Data.wCharges.uses + 2
SpellPackChargedConsumed(Rifleman_Data.wCharges)
elseif (itemId == FourCC('I0AM')) then
Rifleman_Data.eCharges = SpellPackChargedAbilityInit(udg_playerHero[pInt], 'A078', 1, 2, 16.0, 16.0)
SpellPackChargedRefresh(Rifleman_Data.eCharges)
IncUnitAbilityLevel(udg_playerHero[pInt],FourCC('A078'))
elseif (itemId == FourCC('I0AO')) then
IncUnitAbilityLevel(udg_playerHero[pInt],FourCC('A07C'))
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_RIFLEMAN_ITEMBOOL,itemTableUlt)
end
function RiflemanAbilsInit(pInt)
Rifleman_Flare = CreateTrigger()
Rifleman_Shot = CreateTrigger()
Rifleman_Bomb = CreateTrigger()
Rifleman_Vault = CreateTrigger()
Rifleman_Strike = CreateTrigger()
Rifleman_Big = CreateTrigger()
Rifleman_Data.oilCoords = {}
Rifleman_Data.qCharges = SpellPackChargedAbilityInit(udg_playerHero[pInt], 'A077', 0, 3, 8.0, 8.0)
Rifleman_Data.wCharges = SpellPackChargedAbilityInit(udg_playerHero[pInt], 'A079', 0, 2, 12.0, 12.0)
local Rifleman_Flare_f = function()
if LUA_VAR_RIFLEMAN_ITEMBOOL[8] then
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local g = CreateGroup()
GroupEnumUnitsInRange(g, tarX, tarY, 600.0, Condition( function() return BasicDamageExpr(GetOwningPlayer(caster)) end ) )
GroupUtilsAction(g, function() DamagePackageDummyTarget(caster,'A04S','faeriefire', LUA_FILTERUNIT) end)
DestroyGroup(g)
end
end
local Rifleman_Shot_Ignite_Dmg_f = function(u,o)
-- pass this in for ignited missiles
DamagePackageDealAOE(LuaMissileStack[o].ownerUnit, LuaMissileStack[o].missileX, LuaMissileStack[o].missileY,
200.0, LuaMissileStack[o].damage*0.50, false, false,
'', '', '', 1.0)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',
LuaMissileStack[o].missileX, LuaMissileStack[o].missileY))
if LUA_VAR_RIFLEMAN_ITEMBOOL[6] and not LuaMissileStack[o].binding then
LuaMissileStack[o].binding = true
DamagePackageDummyTarget(LuaMissileStack[o].ownerUnit,'A07D','slow',u)
end
end
local Rifleman_Shot_Ignite_f = function(o)
local length = tablelength(Rifleman_Data.oilCoords)
if LuaMissileStack[o].ignited == nil and length > 0 then
for i = 1,length do
if Rifleman_Data.oilCoords[i] ~= nil then
if DistanceBetweenXY(Rifleman_Data.oilCoords[i].x,Rifleman_Data.oilCoords[i].y,
LuaMissileStack[o].missileX, LuaMissileStack[o].missileY) <= 100.0 then
LuaMissileStack[o].ignited = true
-- timer func for LuaMissile to ignite missiles
DestroyEffect(LuaMissileStack[o].effect)
LuaMissileStack[o].effect = AddSpecialEffect('Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl',
LuaMissileStack[o].missileX, LuaMissileStack[o].missileY)
-- to prevent skipping animations, reset height:
BlzSetSpecialEffectZ(LuaMissileStack[o].effect, LuaMissileStack[o].missileZ)
-- assign on hit func:
LuaMissileStack[o].dmgFunc = Rifleman_Shot_Ignite_Dmg_f
if LUA_VAR_RIFLEMAN_ITEMBOOL[5] and not LuaMissileStack[o].enhanced then
LuaMissileStack[o].enhanced = true
LuaMissileStack[o].collides = false
LuaMissileStack[o].damage = LuaMissileStack[o].damage*1.10
end
break
end
end
end
end
if LUA_VAR_RIFLEMAN_ITEMBOOL[1] and not LuaMissileStack[o].reflected
and LuaMissileStack[o].traveled > (LuaMissileStack[o].distance - 40.0) then
LuaMissileStack[o].reflected = true
LuaMissileStack[o].traveled = 0.0
LuaMissileStack[o].angle = LuaMissileStack[o].angle - 180.0
GroupClear(LuaMissileStack[o].damageGroup)
end
end
local Rifleman_Shot_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local maxShots = 3
local range = 600.0
if LUA_VAR_RIFLEMAN_ITEMBOOL[12] then
maxShots = maxShots + 2
end
if LUA_VAR_RIFLEMAN_ITEMBOOL[9] then
range = range + 300.0
end
local angleOffset = 11.0*(maxShots - 1)
local angle = AngleBetweenXY(casterX,casterY,tarX,tarY) - angleOffset/2.0
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*0.40
local str = 'Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl'
local x,y
for i = 1,maxShots do
x,y = PolarProjectionXY(casterX, casterY, range, angle)
LuaMissile.create(caster,x,y,range,0.75,dmg,50.0,true,false,false,nil,nil,str,str,50.0,0.75,Rifleman_Shot_Ignite_f,nil,nil)
angle = angle + 11.0
end
SpellPackChargedConsumed(Rifleman_Data.qCharges)
end
local Rifleman_Bomb_Oil_f = function(u,x,y,d,owner)
-- create pool of oil with timer
local effect = AddSpecialEffect('Abilities\\Spells\\Orc\\LiquidFire\\Liquidfire.mdl',x,y)
-- since a dummy instance gets removed, we need to reference the hero:
local owner = udg_playerHero[GetConvertedPlayerId(GetOwningPlayer(u))]
local dmgInitial = GetHeroStatBJ(bj_HEROSTAT_AGI, owner, true)*0.50
local dmgTime = GetHeroStatBJ(bj_HEROSTAT_AGI, owner, true)*0.15
local i = 0
local duration = 9.0
local length = tablelength(Rifleman_Data.oilCoords) + 1
if LUA_VAR_RIFLEMAN_ITEMBOOL[2] then
dmgInitial = dmgInitial*1.30
duration = duration + 3.0
end
Rifleman_Data.oilCoords[length] = {}
Rifleman_Data.oilCoords[length].x = x
Rifleman_Data.oilCoords[length].y = y
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',x,y))
DamagePackageDealAOE(owner, x, y, 200.0, dmgInitial, false, false, '', '', '', 0.5)
-- only remove charges when the hero uses the ability:
if IsUnitType(u,UNIT_TYPE_HERO) then
end
TimerStart(NewTimer(),1.0,true,function()
DamagePackageDealAOE(owner, x, y, 200.0, dmgTime, false, false, '', '', '', 0.5)
i = i + 1
if i >= duration then
DestroyEffect(effect)
Rifleman_Data.oilCoords[length] = nil
ReleaseTimer() end
end)
end
local Rifleman_Bomb_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local distance = DistanceBetweenXY(casterX, casterY, tarX, tarY)
local x2,y2
local shouldBounce = false
if IsUnitType(caster,UNIT_TYPE_HERO) then
SpellPackChargedConsumed(Rifleman_Data.wCharges)
if LUA_VAR_RIFLEMAN_ITEMBOOL[10] then
shouldBounce = true
end
end
TimerStart(NewTimer(),distance/815,false,function()
-- abil effects after missile lands
Rifleman_Bomb_Oil_f(caster,tarX,tarY)
if shouldBounce then
x2,y2 = PolarProjectionXY(tarX, tarY, distance, AngleBetweenPointsXY(casterX, casterY, tarX, tarY))
DamagePackageDummyAoE(caster, 'A079', 'clusterrockets', nil, tarX, tarY, x2, y2, nil)
end
ReleaseTimer()
end)
end
local Rifleman_Vault_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
if LUA_VAR_RIFLEMAN_ITEMBOOL[14] then
DamagePackageDummyTarget(caster,'A05G','bloodlust')
end
if LUA_VAR_RIFLEMAN_ITEMBOOL[11] then
if Rifleman_Data.qCharges.consumed > 0 then
SpellPackChargedRefresh(Rifleman_Data.qCharges)
end
end
if LUA_VAR_RIFLEMAN_ITEMBOOL[3] then
local length = tablelength(Rifleman_Data.oilCoords)
if length > 0 then
for i = 1,length do
if Rifleman_Data.oilCoords[i] ~= nil then
if DistanceBetweenXY(Rifleman_Data.oilCoords[i].x,Rifleman_Data.oilCoords[i].y,casterX,casterY) <= 100.0 then
tarX,tarY = PolarProjectionXY(tarX,tarY,300.0,AngleBetweenXY(casterX, casterY, tarX, tarY))
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',casterX, casterY))
break
end
end
end
end
end
-- run the leap code; if it fails, reduce charges consumed if charges item is enabled:
if not HeroLeapTargetPoint(caster,0.33,tarX,tarY,nil,nil) and Rifleman_Data.eCharges then
SpellPackChargedRefresh(Rifleman_Data.eCharges)
end
if Rifleman_Data.eCharges then
SpellPackChargedConsumed(Rifleman_Data.eCharges)
end
end
local Rifleman_Strike_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local angle = AngleBetweenPointsXY(casterX,casterY,tarX,tarY)
local distance = DistanceBetweenXY(casterX,casterY,tarX,tarY) -- this can't be over the ability range because of its cast range
local x2,y2 = PolarProjectionXY(tarX, tarY, 600.0, angle) -- 600.0 offset from chosen location
local copter = DamagePackageCreateDummy(GetOwningPlayer(caster), 'h00G', casterX, casterY, angle, 6.0)
local i = 0
SetUnitMoveSpeed(copter,300.0)
IssuePointOrder(copter,"move",tarX,tarY)
TimerStart(NewTimer(),0.03,true,function()
if DistanceBetweenXY(GetUnitX(copter),GetUnitY(copter),tarX,tarY) < 200.0 then
IssuePointOrder(copter,"move",x2,y2)
TimerStart(NewTimer(),0.33,true,function()
i = i + 1
if i < 9 and IsUnitAliveBJ(copter) then
Rifleman_Bomb_Oil_f(caster,GetUnitX(copter),GetUnitY(copter))
else
RemoveUnit(copter)
ReleaseTimer()
end
end)
ReleaseTimer()
elseif not IsUnitAliveBJ(copter) then
ReleaseTimer()
end
end)
if LUA_VAR_RIFLEMAN_ITEMBOOL[16] and IsUnitType(caster,UNIT_TYPE_HERO) then
local x3,y3,x4,y4
x3,y3 = PolarProjectionXY(casterX, casterY, 150.0, angle - 90)
x4,y4 = PolarProjectionXY(x3, y3, distance-3.0, angle) -- nudge distance so it's definitely in range
DamagePackageDummyAoE(caster, 'A07A', 'flamestrike', nil, x3, y3, x4, y4, nil)
x3,y3 = PolarProjectionXY(casterX, casterY, 150.0, angle + 90)
x4,y4 = PolarProjectionXY(x3, y3, distance-3.0, angle)
DamagePackageDummyAoE(caster, 'A07A', 'flamestrike', nil, x3, y3, x4, y4, nil)
end
end
local Rifleman_Big_Hit_f = function(u, o)
-- when Big One hits an enemy do:
DamagePackageDummyTarget(u,'A04O','thunderbolt',u)
end
local Rifleman_Big_Bounds_f = function(o)
if math.fmod(Rifleman_Data.bigEffect,14) == 0 then
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl',
LuaMissileStack[o].missileX, LuaMissileStack[o].missileY))
Rifleman_Data.bigEffect = 1
else
Rifleman_Data.bigEffect = Rifleman_Data.bigEffect + 1
end
-- item effects:
if LUA_VAR_RIFLEMAN_ITEMBOOL[17] and LuaMissileStack[o].reflected == nil and LuaMissileStack[o].traveled > 1600.0 then
LuaMissileStack[o].reflected = true
GroupClear(LuaMissileStack[o].damageGroup) -- allow damage again
LuaMissileStack[o].angle = LuaMissileStack[o].angle - 180.0
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl',
LuaMissileStack[o].missileX, LuaMissileStack[o].missileY))
end
-- destroy missile or do other things when it exits map bounds
if not RectContainsCoords(bj_mapInitialPlayableArea,LuaMissileStack[o].missileX,LuaMissileStack[o].missileY) then
RemoveUnit(Rifleman_Data.bigVision)
LuaMissile.destroy(o)
else
SetUnitX(Rifleman_Data.bigVision,LuaMissileStack[o].missileX)
SetUnitY(Rifleman_Data.bigVision,LuaMissileStack[o].missileY)
end
end
local Rifleman_Big_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*3.50
local str = 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl'
local mis = LuaMissile.create(caster,tarX,tarY,99999.0,1.5,dmg,150.0,false,false,false,nil,Rifleman_Big_Hit_f,
str,str,100.0,2.0,Rifleman_Big_Bounds_f,nil,nil)
Rifleman_Data.bigVision = DamagePackageCreateDummy(GetOwningPlayer(caster), 'e004', casterX, casterY, 270.0, 45.0)
Rifleman_Data.bigEffect = 1
mis.velocity = 20.5
mis = nil
end
SpellPackCreateTrigger(Rifleman_Flare,Rifleman_Flare_f,pInt, 'A07C')
SpellPackCreateTrigger(Rifleman_Shot,Rifleman_Shot_f,pInt, 'A077')
SpellPackCreateTrigger(Rifleman_Bomb,Rifleman_Bomb_f,pInt, 'A079')
SpellPackCreateTrigger(Rifleman_Vault,Rifleman_Vault_f,pInt, 'A078')
SpellPackCreateTrigger(Rifleman_Strike,Rifleman_Strike_f,pInt, 'A07A')
SpellPackCreateTrigger(Rifleman_Big,Rifleman_Big_f,pInt, 'A07B')
end
function WispInit(unit, i)
WispItemsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNWisp.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Wisp_Health_Listener )
EnableTrigger( gg_trg_Wisp_Build )
EnableTrigger( gg_trg_Wisp_Destroy )
EnableTrigger( gg_trg_Wisp_Night )
EnableTrigger( gg_trg_Wisp_Moonlight_Blast )
EnableTrigger( gg_trg_Wisp_Star_Bomb )
EnableTrigger( gg_trg_Wisp_Collapse )
udg_WispUnit = unit
udg_heroCanBecomeVulnerable[i] = false
--DisplayTextToForce( GetPlayersAll(), "Debug: Wisp Initialized")
end
function WispItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I01W", -- 1 (B) Serrated Glaive
"I01U", -- 2 (W) Concentrated Moonlight
"I01V", -- 3 (E) Fulminating Star Residue
"I01X", -- 4 (Trait) Staff of Elune
"I01Y", -- 5 (B) Evanescent Pauldrons
"I068", -- 6 (W) Lunar Beacon
"I067", -- 7 (E) Ethereal Potion
"I06C", -- 8 (Trait) Gust of Elune
"I06E", -- 9 (B) Watcher Rod
"I06D", -- 10 (W) Moon Rod
"I069" -- 11 (E) Star Rod
}
local itemTableUlt = {
"I06B", -- 12 (B) Sentinel Siege Weaponry
"I06G", -- 13 (W) Remnant of Darnassus
"I06H", -- 14 (E) Fallen Star
"I06I", -- 15 (Active) Talisman of Teldrassil
"I06A", -- 16 (R1) Scepter of the World Tree
"I06F" -- 17 (R2) Congealed Dark Matter
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I068")) then IncUnitAbilityLevel(udg_WispUnit,FourCC("A01T"))
elseif (itemId == FourCC("I06A")) then TimerStart(NewTimer(),8.0,true,function()
AdjustPlayerStateBJ(1, GetOwningPlayer(udg_WispUnit), PLAYER_STATE_RESOURCE_LUMBER ) end)
elseif (itemId == FourCC("I06B")) then SetPlayerTechResearchedSwap( FourCC('R00A'), 1, GetOwningPlayer(udg_WispUnit) )
elseif (itemId == FourCC("I06H")) then IncUnitAbilityLevel(udg_WispUnit,FourCC("A01R")) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_wispItemBool,itemTableUlt)
end
function HuntressInit(unit, i)
HuntressItemsInit(i)
HuntressAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNHuntress.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Huntress_Starleap )
EnableTrigger( gg_trg_Huntress_Time_of_Day_Activate )
EnableTrigger( gg_trg_Huntress_Glaive_Net )
EnableTrigger( gg_trg_Huntress_Cleanser )
EnableTrigger( gg_trg_Huntress_Falling_Star )
udg_huntressUnit = unit
end
function HuntressItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I01Z", -- 1 (Q) Solar-Tipped Glaive
"I021", -- 2 (Active) Telescopic Hunting Glass
"I020", -- 3 (E) Huntress Cloak
"I022", -- 4 (Trait) Feathered Armor
"I025", -- 5 (Passive) Spectral Bear Paw
"I06J", -- 6 (W) Hunting Call
"I06K", -- 7 (Active) Shimmering Moonstone
"I06L", -- 8 (Trait) Bouncing Glaive
"I06M", -- 9 (Q) Lunar-Tipped Glaive
"I06N", -- 10 (W) Mark of the Snowy Owl
"I06O" -- 11 (E) Blackened Moonstone
}
local itemTableUlt = {
"I06Q", -- 12 (Q) Glaive of the Third War
"I06S", -- 13 (W) Mark of the Tracker
"I06T", -- 14 (E) Fowling Guise
"I06R", -- 15 (Stats) Exalted Sentinel Pact
"I06U", -- 16 (R1) Starshatter Harness
"I06P" -- 17 (R2) Radiant Star of Elune
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I020")) then IncUnitAbilityLevel(udg_huntressUnit,FourCC("A001"))
elseif (itemId == FourCC("I022")) then SetPlayerTechResearchedSwap( FourCC('R004'), 1, GetOwningPlayer(udg_huntressUnit) )
elseif (itemId == FourCC("I021")) then SetPlayerTechResearchedSwap( FourCC('R005'), 1, GetOwningPlayer(udg_huntressUnit) )
elseif (itemId == FourCC("I025")) then SetPlayerTechResearchedSwap( FourCC('R006'), 1, GetOwningPlayer(udg_huntressUnit) )
elseif (itemId == FourCC("I06L")) then SetPlayerTechResearchedSwap( FourCC('R00B'), 1, GetOwningPlayer(udg_huntressUnit) )
elseif (itemId == FourCC("I06N")) then SetPlayerTechResearchedSwap( FourCC('R00C'), 1, GetOwningPlayer(udg_huntressUnit) )
elseif (itemId == FourCC("I06R")) then HeroAddStatTimer(udg_huntressUnit, 1, 1, 45.0, 15)
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_huntressItemBool,itemTableUlt)
end
function HuntressAbilsInit(pInt)
Huntress_Owl = CreateTrigger()
Huntress_Leap = CreateTrigger()
local Huntress_Owl_f = function()
local caster = GetTriggerUnit()
local x = GetUnitX(caster)
local y = GetUnitY(caster)
local tarX = GetSpellTargetX()
local tarY = GetSpellTargetY()
if (udg_huntressItemBool[6]) then
local g = CreateGroup()
GroupEnumUnitsInRange(g, x, y, 800.0, Condition( function() return
IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
DamagePackageDummyTarget(caster,'A045','bloodlust')
end
DestroyGroup(g)
end
if (udg_huntressItemBool[13]) then
local x2
local y2
local u
local facing = AngleBetweenPointsXY(x,y,tarX,tarY)
x2,y2 = PolarProjectionXY(tarX,tarY,800.0,facing)
u = CreateUnit(GetOwningPlayer(caster),FourCC('e001'),x2,y2,270.0)
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
x2,y2 = PolarProjectionXY(tarX,tarY,1600.0,facing)
u = CreateUnit(GetOwningPlayer(caster),FourCC('e001'),x2,y2,270.0)
UnitApplyTimedLifeBJ( 30.0, FourCC('BTLF'), u )
end
end
Huntress_Leap_Item_f = function()
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\NightElf\\NEDeathSmall\\NEDeathSmall.mdl',GetUnitX(udg_huntressUnit), GetUnitY(udg_huntressUnit)))
DamagePackageDealAOE(udg_huntressUnit, GetUnitX(udg_huntressUnit), GetUnitY(udg_huntressUnit), 200.0,
GetHeroStatBJ(bj_HEROSTAT_AGI, udg_huntressUnit, true)*2.0, false, false, '', '',
'Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl', 1.33)
end
local Huntress_Leap_f = function()
local x = GetSpellTargetX()
local y = GetSpellTargetY()
if (udg_huntressItemBool[16]) then
if (not HeroLeapTargetPoint(GetTriggerUnit(),0.36,x,y,nil,Huntress_Leap_Item_f)) then
DamagePackageAddMana(GetTriggerUnit(),50) -- returns false on invalid; if so, refund mana
end
else
if (not HeroLeapTargetPoint(GetTriggerUnit(),0.36,x,y,nil,nil)) then
DamagePackageAddMana(GetTriggerUnit(),50)
end
end
end
-- spectral owl (currently separate from GUI)
TriggerRegisterPlayerUnitEvent(Huntress_Owl,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Huntress_Owl, Filter( function() return GetSpellAbilityId() == FourCC('A006') end ) )
TriggerAddAction(Huntress_Owl,Huntress_Owl_f)
TriggerRegisterPlayerUnitEvent(Huntress_Leap,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Huntress_Leap, Filter( function() return GetSpellAbilityId() == FourCC('A05X') end ) )
TriggerAddAction(Huntress_Leap,Huntress_Leap_f)
end
function DruidClawInit(unit, i)
LUA_VAR_DRUIDCLAW_UNIT = unit
LUA_VAR_DRUIDCLAW_ITEMBOOL = {}
DruidClawItemsInit(i)
DruidClawAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNDruidOfTheClaw.blp'
LeaderboardUpdateHeroIcon(i)
LUA_TRG_DRUIDCLAW_MAX_MANA = ItemShopSetManaCap(i, 100)
end
function DruidClawItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I090", -- 1 (Q) Blooming Rod
"I091", -- 2 (W) Bear Armor
"I092", -- 3 (E) Living Bark
"I093", -- 4 (Trait) Moon Stone
"I096", -- 5 (Q) Mending Rod
"I094", -- 6 (W) Medallion of the Claw
"I098", -- 7 (E) Vengeful Druid Claws
"I095", -- 8 (Trait) Warden's Dagger
"I099", -- 9 (Q) Trinket of Channeling
"I097", -- 10 (W) Bark Stompers
"I09A" -- 11 (E) Talisman of the Claw
}
local itemTableUlt = {
"I09C", -- 12 (Q) Malorne's Trinket of Life
"I09B", -- 13 (W) Ursol's Trinket of Rage
"I09D", -- 14 (E) Tortolla's Trinket of Fortitude
"I09E", -- 15 (Trait) Uroc's Trinket of Rending
"I09F", -- 16 (R1) Ashamane's Trinket of Ferocity
"I09G" -- 17 (R2) Aessina's Trinket of Lifeblood
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I099")) then IncUnitAbilityLevel(LUA_VAR_DRUIDCLAW_UNIT,FourCC("A06H"))
elseif (itemId == FourCC("I09A")) then IncUnitAbilityLevel(LUA_VAR_DRUIDCLAW_UNIT,FourCC("A06K"))
elseif (itemId == FourCC("I09B")) then IncUnitAbilityLevel(LUA_VAR_DRUIDCLAW_UNIT,FourCC("A06I"))
elseif (itemId == FourCC("I093")) then DestroyTrigger(LUA_TRG_DRUIDCLAW_MAX_MANA)
LUA_TRG_DRUIDCLAW_MAX_MANA = ItemShopSetManaCap(GetConvertedPlayerId(GetOwningPlayer(LUA_VAR_DRUIDCLAW_UNIT)), 125)
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_DRUIDCLAW_ITEMBOOL,itemTableUlt)
end
function DruidClawAbilsInit(pInt)
DruidClaw_Trait = CreateTrigger()
DruidClaw_Rejuvenation = CreateTrigger()
DruidClaw_Roar = CreateTrigger()
DruidClaw_Earthward = CreateTrigger()
DruidClaw_Guardian = CreateTrigger()
DruidClaw_Seed = CreateTrigger()
DruidClaw_Maul = CreateTrigger()
DruidClaw_Thrash = CreateTrigger()
DruidClaw_Form = CreateTrigger()
LUA_VAR_DRUIDCLAW_REJUV_TIMER = {}
LUA_VAR_DRUIDCLAW_EARTHWARD_TIMER = {}
LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG = {}
local DruidClaw_Form_f = function()
local caster = GetTriggerUnit()
local typeId = GetUnitTypeId(caster)
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[2]) then
DamagePackageDummyTarget(caster,'A04J','innerfire')
end
-- abils get overwritten by form changes, so we have to do something tacky/persistent when exiting forms:
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[9] and typeId == FourCC('O01D')) then
TimerStart(NewTimer(),0.45,false,function() IncUnitAbilityLevel(LUA_VAR_DRUIDCLAW_UNIT,FourCC("A06H")) ReleaseTimer() end)
end
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[11] and typeId == FourCC('O01D')) then
TimerStart(NewTimer(),0.45,false,function() IncUnitAbilityLevel(LUA_VAR_DRUIDCLAW_UNIT,FourCC("A06K")) ReleaseTimer() end)
end
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[15] and typeId == FourCC('O01C')) then
TimerStart(NewTimer(),0.45,false,function() UnitRemoveBuffBJ( FourCC('Brej'), caster ) ReleaseTimer() end)
end
end
local DruidClaw_Rejuvenation_f = function()
local target = GetSpellTargetUnit()
local ID = GetUnitUserData(target)
local caster = GetTriggerUnit()
local healing = 1.25
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[5]) then
healing = healing*1.1
end
if (LUA_VAR_DRUIDCLAW_REJUV_TIMER[ID] ~= nil) then ReleaseTimer(LUA_VAR_DRUIDCLAW_REJUV_TIMER[ID]) end
LUA_VAR_DRUIDCLAW_REJUV_TIMER[ID] = NewTimer()
TimerStart(LUA_VAR_DRUIDCLAW_REJUV_TIMER[ID],1.0,true,function()
if (UnitHasBuffBJ(target,FourCC('Brej')) and IsUnitAliveBJ(target)) then
DamagePackageRestoreHealth(target,healing,true,'_')
else
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[1]) then
DamagePackageRestoreHealth(target,GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*0.50,false,'_',caster)
end
ReleaseTimer()
end
end)
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[1]) then
DamagePackageRestoreHealth(target,GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*0.50,false,'_',caster)
end
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[12]) then
local seedDummy = DamagePackageCreateDummy(GetOwningPlayer(caster),'e00S',GetUnitX(target),GetUnitY(target),270.0, 3.1)
local seedHeal = GetHeroStatBJ(bj_HEROSTAT_INT, caster, true)*1.50
TimerStart(NewTimer(),3.0,false,function()
DamagePackageDealAOE(caster, GetUnitX(seedDummy), GetUnitY(seedDummy),
300.0, seedHeal, true, false, '', '', 'Abilities\\Spells\\Other\\AcidBomb\\BottleMissile.mdl', 1.0)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\AcidBomb\\BottleMissile.mdl',GetUnitX(seedDummy),GetUnitY(seedDummy)))
RemoveUnit(seedDummy)
ReleaseTimer()
end)
end
end
local DruidClaw_Roar_f = function()
local caster = GetTriggerUnit()
local g = CreateGroup()
GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),600.0, Filter(function() return
IsUnitAlly(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO)
and GetFilterUnit() ~= caster end ) )
GroupUtilsAction(g,function()
DamagePackageAddMana(LUA_FILTERUNIT,10.0,true)
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\Bolt\\BoltImpact.mdl',GetUnitX(LUA_FILTERUNIT),GetUnitY(LUA_FILTERUNIT)))
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[10] and UnitHasBuffBJ(LUA_FILTERUNIT,FourCC('Brej'))) then
DamagePackageDummyTarget(LUA_FILTERUNIT,'A029','bloodlust')
end
end)
DestroyGroup(g)
if (GetUnitTypeId(caster) == FourCC('O01D')) then
DamagePackageAddMana(caster,10.0,true)
else
DamagePackageRestoreHealth(caster,10.0,true,'_',caster)
end
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[6]) then
BlzEndUnitAbilityCooldown(caster,FourCC('A06L'))
end
end
DruidClaw_Earthward_Listener_f = function()
DamagePackageRestoreHealth(udg_DamageEventTarget,0.75,true,'_',udg_playerHero[pInt])
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[7]) then
UnitDamageTargetBJ(udg_DamageEventTarget,udg_DamageEventSource,
GetHeroStatBJ(bj_HEROSTAT_STR, udg_playerHero[pInt], true)*0.20,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
end
local DruidClaw_Earthward_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local ID = GetUnitUserData(target)
local dur = 8.0
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[11]) then dur = dur + 4.0 end
local cond = function() return
IsUnitEnemy(udg_DamageEventSource,GetOwningPlayer(target))
and not IsUnitType(udg_DamageEventSource,UNIT_TYPE_ANCIENT)
and udg_DamageEventTarget == target
end
if (LUA_VAR_DRUIDCLAW_EARTHWARD_TIMER[ID] ~= nil) then ReleaseTimer(LUA_VAR_DRUIDCLAW_EARTHWARD_TIMER[ID]) end
LUA_VAR_DRUIDCLAW_EARTHWARD_TIMER[ID] = NewTimer()
if (LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG[ID] ~= nil) then DestroyTrigger(LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG[ID]) end
LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG[ID] = CreateTrigger()
SpellPackCreateDamageTrigger(LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG[ID],DruidClaw_Earthward_Listener_f,1,cond,"udg_DamageEvent")
TimerStart(LUA_VAR_DRUIDCLAW_EARTHWARD_TIMER[ID],dur,false,function()
DestroyTrigger(LUA_VAR_DRUIDCLAW_EARTHWARD_TRIG[ID])
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[14]) then
DamagePackageRestoreHealth(target,dur,true,nil,caster)
end
ReleaseTimer()
end)
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[3]) then
DamagePackageDummyTarget(target,'A04J','innerfire')
end
end
local DruidClaw_Guardian_f = function()
local caster = GetTriggerUnit()
local i = 0
if (GetUnitTypeId(caster) == FourCC('O01C')) then -- if not bear form yet, activate it
BlzEndUnitAbilityCooldown(caster,FourCC('A06J'))
IssueImmediateOrder(caster,'bearform')
end
TimerStart(NewTimer(),0.5,true,function()
DamagePackageAddMana(caster,-7.0,false)
i = i + 1
if (not UnitHasBuffBJ(caster,FourCC('B01S')) or not IsUnitAliveBJ(caster)) then
ReleaseTimer()
elseif (GetUnitManaPercent(caster) < 1.00) then
UnitRemoveBuffBJ( FourCC('B01S'), caster )
elseif (math.fmod(i,2) == 0) then
BlzEndUnitAbilityCooldown(caster,FourCC('A06L'))
end
end)
end
local DruidClaw_Seed_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local healing = 30.0
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[17]) then
healing = 60.0
end
local i = 0
TimerStart(NewTimer(),0.15,true,function()
i = i + 1
if (not IsUnitAliveBJ(target)) then
ReleaseTimer()
elseif (i >= 20) then
DamagePackageRestoreHealth(target,healing,true,nil,caster)
ReleaseTimer()
elseif (GetUnitLifePercent(target) < 15.0) then
DamagePackageRestoreHealth(target,healing*2.0,true,'Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl',caster)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\NightElf\\MoonWell\\MoonWellCasterArt.mdl',GetUnitX(target),GetUnitY(target)))
ReleaseTimer()
end
end)
end
local DruidClaw_Maul_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*1.75
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[15]) then
dmg = dmg*1.15
end
if (UnitHasBuffBJ(caster,FourCC('B01S'))) then
local g = CreateGroup()
dmg = dmg*1.15
local healing = dmg
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[16]) then
healing = healing*2.0
end
GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),600.0, Filter(function() return
DamagePackageAllyFilter(GetFilterUnit(),GetOwningPlayer(caster)) end ) )
GroupUtilsAction(g,function()
DamagePackageRestoreHealth(LUA_FILTERUNIT,healing,false,'Abilities\\Weapons\\Bolt\\BoltImpact.mdl',caster)
end)
DestroyGroup(g)
end
UnitDamageTargetBJ(caster,target,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
local DruidClaw_Thrash_f = function()
local caster = GetTriggerUnit()
local target = GetSpellTargetUnit()
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*2.00
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[15]) then
dmg = dmg*1.15
end
UnitDamageTargetBJ(caster,target,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
DamagePackageDummyTarget(caster,'A045','bloodlust')
if (LUA_VAR_DRUIDCLAW_ITEMBOOL[8]) then
DamagePackageDummyTarget(caster,'A04B','slow',target)
DamagePackageAddMana(caster,5,false)
end
end
SpellPackCreateTrigger(DruidClaw_Rejuvenation,DruidClaw_Rejuvenation_f,pInt,'A06H')
SpellPackCreateTrigger(DruidClaw_Roar,DruidClaw_Roar_f,pInt,'A06I')
SpellPackCreateTrigger(DruidClaw_Earthward,DruidClaw_Earthward_f,pInt,'A06K')
SpellPackCreateTrigger(DruidClaw_Guardian,DruidClaw_Guardian_f,pInt,'A06O')
SpellPackCreateTrigger(DruidClaw_Seed,DruidClaw_Seed_f,pInt,'A06N')
SpellPackCreateTrigger(DruidClaw_Maul,DruidClaw_Maul_f,pInt,'A06L')
SpellPackCreateTrigger(DruidClaw_Thrash,DruidClaw_Thrash_f,pInt,'A06M')
SpellPackCreateTrigger(DruidClaw_Form,DruidClaw_Form_f,pInt,'A06J')
end
function ForestTrollWarlordInit(unit, i)
LUA_VAR_FTW_UNIT = unit
LUA_VAR_FTW_ITEMBOOL = {}
ForestTrollWarlord_Data = {}
ForestTrollWarlordItemsInit(i)
ForestTrollWarlordAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNForestTroll.blp'
LeaderboardUpdateHeroIcon(i)
udg_heroCanRegenMana[i] = false
LUA_TRG_FTW_MAX_MANA = ItemShopSetManaCap(i, 100)
end
function ForestTrollWarlordItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I0AW", -- 1 (Q) Energized Pendant
"I0AZ", -- 2 (W) Mojo of Fire
"I0B2", -- 3 (E) Shuffling Hook
"I0B5", -- 4 (Trait) Toeless Jumpers
"I0AX", -- 5 (Q) Ring of Fangs
"I0B0", -- 6 (W) Mojo of Frost
"I0B3", -- 7 (E) Weightless Troll Garb
"I0B6", -- 8 (Trait) Fencing Plate
"I0AY", -- 9 (Q) Pocket Axe
"I0B1", -- 10 (W) Mojo of Fibers
"I0B4" -- 11 (E) Armor-Piercing Barbs
}
local itemTableUlt = {
"I0B7", -- 12 (Q) Harrowing Blade
"I0B8", -- 13 (W) Mojo of Vampirism
"I0B9", -- 14 (E) Spirit Sling
"I0BA", -- 15 (Trait) Trinket of Jammin'
"I0BB", -- 16 (R1) Skull Breaker
"I0BC" -- 17 (R2) Blood Poker
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I0B3")) then IncUnitAbilityLevel(LUA_VAR_FTW_UNIT,FourCC("A07G"))
elseif (itemId == FourCC("I0B5")) then ForestTrollWarlord_Data.acrobatRange = 300.0
elseif (itemId == FourCC("I0AY")) then
ForestTrollWarlord_Data.qCharges.uses = ForestTrollWarlord_Data.qCharges.uses + 1
SpellPackChargedRefresh(ForestTrollWarlord_Data.qCharges)
elseif (itemId == FourCC("I0BB")) then ForestTrollWarlord_Data.rendingRequired = 2
elseif (itemId == FourCC("I0BB")) then
DestroyTrigger(LUA_TRG_FTW_MAX_MANA)
LUA_TRG_FTW_MAX_MANA = ItemShopSetManaCap(GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit())), 125) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_FTW_ITEMBOOL,itemTableUlt)
end
function ForestTrollWarlordAbilsInit(pInt)
ForestTrollWarlord_Acrobatic = CreateTrigger()
ForestTrollWarlord_Rending = CreateTrigger()
ForestTrollWarlord_Taz = CreateTrigger()
ForestTrollWarlord_Grapple = CreateTrigger()
ForestTrollWarlord_Skull = CreateTrigger()
ForestTrollWarlord_Flurry = CreateTrigger()
ForestTrollWarlord_Skull_learn = CreateTrigger()
ForestTrollWarlord_Flurry_learn = CreateTrigger()
ForestTrollWarlord_Auto = CreateTrigger()
ForestTrollWarlord_Data.qCharges = SpellPackChargedAbilityInit(udg_playerHero[pInt], 'A07E', 0, 2, 12.0, 12.0)
ForestTrollWarlord_Data.traitTimer = NewTimer()
ForestTrollWarlord_Data.flurryTimer = NewTimer()
ForestTrollWarlord_Data.cleaver = 0 -- # skull cleaver active
ForestTrollWarlord_Data.rendingCasts = 0 -- skull cleaver proc
ForestTrollWarlord_Data.rendingRequired = 3 -- casts required
ForestTrollWarlord_Data.R1 = false
ForestTrollWarlord_Data.R2 = false
ForestTrollWarlord_Data.fireAttacks = 0
ForestTrollWarlord_Data.frostAttacks = 0
ForestTrollWarlord_Data.fiberAttacks = 0
ForestTrollWarlord_Data.acrobatRange = 200.0
SpellPackChargedRefresh(ForestTrollWarlord_Data.qCharges)
ForestTrollWarlord_Acrobatic_l = CreateTrigger()
TriggerRegisterPlayerUnitEvent(ForestTrollWarlord_Acrobatic_l,Player(pInt-1),EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, nil)
TriggerAddCondition(ForestTrollWarlord_Acrobatic_l, Condition( function() return
GetOrderedUnit() == udg_playerHero[pInt]
and (GetIssuedOrderIdBJ() == 851986 or GetIssuedOrderIdBJ() == 851971) end)) -- 'move' or 'smart'
TriggerAddAction(ForestTrollWarlord_Acrobatic_l, function()
local caster = GetOrderedUnit()
local tarX = GetOrderPointX()
local tarY = GetOrderPointY()
local landX,landY = PolarProjectionXY(GetUnitX(caster),GetUnitY(caster),ForestTrollWarlord_Data.acrobatRange,
AngleBetweenXY(GetUnitX(caster),GetUnitY(caster),tarX,tarY))
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl',landX,landY))
HeroLeapTargetPoint(caster,0.15,landX,landY,nil,nil)
PauseTimer(ForestTrollWarlord_Data.traitTimer)
if LUA_VAR_FTW_ITEMBOOL[8] then DamagePackageDummyTarget(caster,'A053','innerfire') end
if LUA_VAR_FTW_ITEMBOOL[15] then
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl',GetUnitX(caster),GetUnitY(caster)))
UnitAddAbility(caster, FourCC('A07O')) -- 100% evasion
TimerStart(NewTimer(),0.33,false,function() UnitRemoveAbility(caster, FourCC('A07O')) ReleaseTimer() end)
end
DisableTrigger(ForestTrollWarlord_Acrobatic_l)
end)
DisableTrigger(ForestTrollWarlord_Acrobatic_l)
-- on-use trait
local ForestTrollWarlord_Acrobatic_f = function()
ForestTrollWarlord_Acrobatic_activate()
end
-- activate dash
function ForestTrollWarlord_Acrobatic_activate()
EnableTrigger(ForestTrollWarlord_Acrobatic_l)
TimerStart(ForestTrollWarlord_Data.traitTimer,6.0,false,function()
DisableTrigger(ForestTrollWarlord_Acrobatic_l)
end)
end
-- convert to melee attacks
function ForestTrollWarlord_melee_activate(u)
SetPlayerTechResearchedSwap( FourCC('R00D'), 1, GetOwningPlayer(u) )
TimerStart(ForestTrollWarlord_Data.flurryTimer,12.0,false,function()
ForestTrollWarlord_melee_deactivate(u)
end)
DamagePackageDummyTarget(u,'A07P','bloodlust') -- add melee eyecandy buff
end
-- convert to ranged attacks
function ForestTrollWarlord_melee_deactivate(u)
SetPlayerTechResearchedSwap( FourCC('R00D'), 0, GetOwningPlayer(u) )
PauseTimer(ForestTrollWarlord_Data.flurryTimer)
UnitRemoveBuffBJ( FourCC('B01Y'), u ) -- remove melee eyecandy buff
end
local ForestTrollWarlord_Rending_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local dmgEffect = 'Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl'
local dmg = GetHeroStatBJ(bj_HEROSTAT_AGI, caster, true)*1.15
local dur = 6.0
if LUA_VAR_FTW_ITEMBOOL[5] and DistanceBetweenXY(casterX, casterY, tarX, tarY) >= 400.0 then
dmg = dmg*1.25
end
if LUA_VAR_FTW_ITEMBOOL[12] then
dmg = dmg*1.2
dur = dur + 6.0
end
if ForestTrollWarlord_Data.R1 then -- hero has Skull Cleaver learned
ForestTrollWarlord_Data.rendingCasts = ForestTrollWarlord_Data.rendingCasts + 1
if ForestTrollWarlord_Data.rendingCasts > ForestTrollWarlord_Data.rendingRequired then -- every 4th use, activate Skull Cleaver
ForestTrollWarlord_Data.rendingCasts = 0
ForestTrollWarlord_Skull_update(1)
end
end
local landFunc = function(x,y)
-- land effect:
DestroyEffect(AddSpecialEffect(dmgEffect,x,y))
DamagePackageDealAOE(caster, x, y, 150.0, dmg, false, false, '', '', dmgEffect, 1.0)
-- axe collection glow eye candy:
local glow = AddSpecialEffect('Abilities\\Spells\\Human\\Brilliance\\Brilliance.mdl',x,y)
BlzSetSpecialEffectScale( glow, 0.6 )
BlzSetSpecialEffectAlpha( glow, 175 )
-- axe dummy:
local axe = DamagePackageCreateDummy(GetOwningPlayer(caster),'e00V', x, y, AngleBetweenXY(casterX,casterY,tarX,tarY), 6.0)
local trig = CreateTrigger()
-- collection trigger:
TriggerRegisterUnitInRangeSimple( trig, 65.0, axe )
TriggerAddCondition(trig, Condition( function() return GetTriggerUnit() == caster end ) )
-- collect func:
TriggerAddAction(trig, function()
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl',GetUnitX(caster), GetUnitY(caster)))
if UnitHasBuffBJ(caster,FourCC('B01W')) then -- Jungle Flurry active, buff AS and MS.
if UnitHasBuffBJ(caster,FourCC('B01G')) then -- apply again after 3 sec if it should "stack" on 2 quick collections.
TimerStart(NewTimer(),3.0,false,function()
DamagePackageDummyTarget(caster,'A063','bloodlust') ReleaseTimer() end)
end
DamagePackageDummyTarget(caster,'A063','bloodlust')
else -- normal collection, reset charges
SpellPackChargedRefresh(ForestTrollWarlord_Data.qCharges)
end
if axe then RemoveUnit(axe) end
if glow then BlzSetSpecialEffectZ(glow, 3000) DestroyEffect(glow) end -- effect doesn't fade immediately, so do Z trick.
-- if a charge is recovered, convert to ranged:
if LUA_VAR_FTW_ITEMBOOL[1] then DamagePackageAddMana(caster,10,false) end
end)
TimerStart(NewTimer(),dur,false,function()
if axe then RemoveUnit(axe) end
if glow then BlzSetSpecialEffectZ(glow, 3000) DestroyEffect(glow) end
ReleaseTimer()
end)
-- wait to pause anim, orient axe in the ground:
TimerStart(NewTimer(),0.12,false,function() SetUnitTimeScalePercent( axe, 0.00 ) ReleaseTimer() end)
end
if ForestTrollWarlord_Data.cleaver <= 0 then -- R2
SpellPackParabolicMissile(casterX,casterY,tarX,tarY,250.0,0.75,'Abilities\\Weapons\\Axe\\AxeMissile.mdl',1.33,landFunc)
else
g = CreateGroup()
local cleaverFunc = function(x,y)
GroupEnumUnitsInRange(g,x,y,150.0, Condition(function() return
BasicDamageExpr(GetOwningPlayer(caster))
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) end))
GroupUtilsAction(g, function()
local dmg = GetUnitState(LUA_FILTERUNIT, UNIT_STATE_MAX_LIFE)*0.08
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Orc\\OrcSmallDeathExplode\\OrcSmallDeathExplode.mdl',
GetUnitX(LUA_FILTERUNIT),GetUnitY(LUA_FILTERUNIT)))
UnitDamageTargetBJ(caster,LUA_FILTERUNIT,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
DamagePackageDummyTarget(caster,'A009','slow',LUA_FILTERUNIT)
end)
DestroyGroup(g)
end
local e = SpellPackParabolicMissile(casterX,casterY,tarX,tarY,250.0,0.75,'Abilities\\Weapons\\Axe\\AxeMissile.mdl',2.25,landFunc)
BlzSetSpecialEffectColor(e, 255, 90, 90)
-- additional special effect:
SpellPackParabolicMissile(casterX,casterY,tarX,tarY,250.0,0.75,'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl',1.25,cleaverFunc)
ForestTrollWarlord_Skull_update(-1)
end
SpellPackChargedConsumed(ForestTrollWarlord_Data.qCharges)
-- if charges are expended, convert to melee:
if ForestTrollWarlord_Data.qCharges.consumed >= ForestTrollWarlord_Data.qCharges.uses then
ForestTrollWarlord_melee_activate(caster)
end
-- activate trait:
ForestTrollWarlord_Acrobatic_activate()
end
local ForestTrollWarlord_Taz_f = function()
local caster, casterX, casterY = SpellPackSetupInstantAbil()
DamagePackageDummyTarget(caster,'A07N','bloodlust')
DamagePackageDummyAoE(caster,'A07K','roar')
ForestTrollWarlord_Data.fireAttacks = 0
ForestTrollWarlord_Data.frostAttacks = 0
ForestTrollWarlord_Data.fiberAttacks = 0
end
local ForestTrollWarlord_Grapple_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local target = GetSpellTargetUnit() or nil
if LUA_VAR_FTW_ITEMBOOL[3] and target and IsUnitAlly(target, GetOwningPlayer(caster))
and IsUnitType(target,UNIT_TYPE_HERO) and not IsUnitType(target,UNIT_TYPE_ANCIENT) then
-- ally effects:
HeroLeapTargetPoint(target,0.33,casterX,casterY,nil,nil)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',GetUnitX(target), GetUnitY(target)))
elseif target and IsUnitEnemy(target, GetOwningPlayer(caster))
and not IsUnitType(target,UNIT_TYPE_ANCIENT) then
if (not HeroLeapTargetPoint(caster,0.33,tarX,tarY,nil,nil)) then
DamagePackageAddMana(caster,50)
else
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',GetUnitX(target), GetUnitY(target)))
-- enemy effects:
if LUA_VAR_FTW_ITEMBOOL[11] then
DamagePackageDummyTarget(caster,'A04S','faeriefire',target)
end
if LUA_VAR_FTW_ITEMBOOL[14] then
DamagePackageDummyTarget(caster,'A03H','thunderbolt',target)
TimerStart(NewTimer(),1.0,false,function() DamagePackageDummyTarget(caster,'A009','slow',target) end)
end
end
else
-- target ground:
if (not HeroLeapTargetPoint(caster,0.33,tarX,tarY,nil,nil)) then
DamagePackageAddMana(caster,50)
end
end
end
-- amount = 1 or -1
function ForestTrollWarlord_Skull_update(amount) -- don't allow more than 2 stored charges
ForestTrollWarlord_Data.cleaver = ForestTrollWarlord_Data.cleaver + amount
if ForestTrollWarlord_Data.cleaver > 2 then
ForestTrollWarlord_Data.cleaver = 2
elseif ForestTrollWarlord_Data.cleaver < 0 then
ForestTrollWarlord_Data.cleaver = 0
end
end
local ForestTrollWarlord_Skull_f = function()
ForestTrollWarlord_melee_deactivate(GetTriggerUnit())
SpellPackChargedRefresh(ForestTrollWarlord_Data.qCharges)
ForestTrollWarlord_Data.qCharges.consumed = 0
ForestTrollWarlord_Skull_update(1)
end
local ForestTrollWarlord_Flurry_f = function()
local caster, tarX, tarY, target, casterX, casterY = SpellPackSetupTargetUnitAbil()
if (not HeroLeapTargetPoint(caster,0.33,tarX,tarY,nil,nil)) then
DamagePackageAddMana(caster,50)
else
-- wait for charge to land:
TimerStart(NewTimer(),0.33,false,function()
DamagePackageDummyTarget(caster,'A07M','slow',target)
IssueTargetOrderById(caster,851983,target)
ReleaseTimer()
end)
end
end
ForestTrollWarlord_Auto_f = function()
if UnitHasBuffBJ(udg_DamageEventSource,FourCC('B01X')) then
if LUA_VAR_FTW_ITEMBOOL[2] then -- fire
ForestTrollWarlord_Data.fireAttacks = ForestTrollWarlord_Data.fireAttacks + 1
if math.fmod(ForestTrollWarlord_Data.fireAttacks,2) == 0 then
ForestTrollWarlord_Data.fireAttacks = 0
DamagePackageDealAOE(udg_DamageEventSource, GetUnitX(udg_DamageEventTarget), GetUnitY(udg_DamageEventTarget),
150.0, udg_DamageEventAmount*0.50, false, false, '', '', 'Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl', 0.7)
end
end
if LUA_VAR_FTW_ITEMBOOL[6] then -- frost
ForestTrollWarlord_Data.frostAttacks = ForestTrollWarlord_Data.frostAttacks + 1
if math.fmod(ForestTrollWarlord_Data.frostAttacks,2) == 0 then
ForestTrollWarlord_Data.frostAttacks = 0
DamagePackageDealAOE(udg_DamageEventSource, GetUnitX(udg_DamageEventTarget), GetUnitY(udg_DamageEventTarget),
150.0, udg_DamageEventAmount*0.25, false, true, 'A07D', 'slow', 'Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl', 0.7)
end
end
if LUA_VAR_FTW_ITEMBOOL[10] then -- fibers
ForestTrollWarlord_Data.fiberAttacks = ForestTrollWarlord_Data.fiberAttacks + 1
if math.fmod(ForestTrollWarlord_Data.fiberAttacks,2) == 0 then
ForestTrollWarlord_Data.fiberAttacks = 0
DamagePackageRestoreHealth(udg_DamageEventSource,10.0,true,'Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl',udg_DamageEventSource)
end
end
if LUA_VAR_FTW_ITEMBOOL[13] then
DamagePackageRestoreHealth(udg_DamageEventSource,udg_DamageEventAmount*0.35,false,
'Objects\\Spawnmodels\\Other\\BeastmasterBlood\\BeastmasterBlood.mdl',udg_DamageEventSource)
end
if LUA_VAR_FTW_ITEMBOOL[17] and UnitHasBuffBJ(udg_DamageEventSource,FourCC('B01W')) then -- has jungle flurry
UnitDamageTargetBJ(udg_DamageEventSource,udg_DamageEventTarget,GetUnitState(udg_DamageEventTarget,UNIT_STATE_MAX_LIFE)*0.02,
ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Orc\\OrcSmallDeathExplode\\OrcSmallDeathExplode.mdl',
GetUnitX(udg_DamageEventTarget),GetUnitY(udg_DamageEventTarget)))
end
end
end
local boolexpr = function() return udg_DamageEventSource == udg_playerHero[pInt] and udg_DamageEventAttackT == udg_ATTACK_TYPE_HERO
and udg_IsDamageMelee end
SpellPackCreateDamageTrigger(ForestTrollWarlord_Auto,ForestTrollWarlord_Auto_f,pInt,boolexpr,nil)
SpellPackCreateTrigger(ForestTrollWarlord_Acrobatic,ForestTrollWarlord_Acrobatic_f,pInt,'A07L')
SpellPackCreateTrigger(ForestTrollWarlord_Rending,ForestTrollWarlord_Rending_f,pInt, 'A07E')
SpellPackCreateTrigger(ForestTrollWarlord_Taz,ForestTrollWarlord_Taz_f,pInt, 'A07F')
SpellPackCreateTrigger(ForestTrollWarlord_Grapple,ForestTrollWarlord_Grapple_f,pInt, 'A07G')
SpellPackCreateTrigger(ForestTrollWarlord_Skull,ForestTrollWarlord_Skull_f,pInt, 'A07H')
SpellPackCreateTrigger(ForestTrollWarlord_Flurry,ForestTrollWarlord_Flurry_f,pInt, 'A07I')
local R1func = function() ForestTrollWarlord_Data.R1 = true end
local R2func = function() ForestTrollWarlord_Data.R2 = true end
SpellPackCreateLearnTrigger(ForestTrollWarlord_Skull_learn, pInt, 'A07H', R1func)
SpellPackCreateLearnTrigger(ForestTrollWarlord_Flurry_learn, pInt, 'A07I', R2func)
end
function FurbolgInit(unit, i)
LUA_VAR_FURBOLGUNIT = unit
FurbolgItemsInit(i)
FurbolgAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNFurbolg.blp'
LeaderboardUpdateHeroIcon(i)
end
function FurbolgItemsInit(pInt)
LUA_VAR_FURBOLGITEMBOOL = {}
-- raw code and descriptions
local itemTable = {
"I08J", -- 1 (Q) Lost Spirit Stone
"I08Q", -- 2 (W) Potion of Fury
"I08P", -- 3 (E) Bifurcated Ring
"I08R", -- 4 (Trait) Cured Star Moss
"I08K", -- 5 (Q) Fungal Spider Ring
"I08S", -- 6 (W) Potion of Binding
"I08O", -- 7 (E) Mending Rod
"I08T", -- 8 (Trait) Cured Riverbud
"I08L", -- 9 (Q) Tome of Shamanism
"I08M", -- 10 (W) Tome of Conscription
"I08N" -- 11 (E) Tome of Protection
}
local itemTableUlt = {
"I08V", -- 12 (Q) Mark of the Druid
"I08W", -- 13 (W) Mark of the Beast
"I08X", -- 14 (E) Mark of the Defender
"I08U", -- 15 (Trait) Mark of the Grizzly
"I08Z", -- 16 (R1) Mark of the Brute
"I08Y" -- 17 (R2) Mark of the Ursa
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I08L")) then IncUnitAbilityLevel(LUA_VAR_FURBOLGUNIT,FourCC("A06A"))
elseif (itemId == FourCC("I08M")) then IncUnitAbilityLevel(LUA_VAR_FURBOLGUNIT,FourCC("A06C"))
elseif (itemId == FourCC("I08N")) then IncUnitAbilityLevel(LUA_VAR_FURBOLGUNIT,FourCC("A06B"))
elseif (itemId == FourCC("I08T")) then LUA_VAR_FURBOLGFRENZYMULT = 0.20
elseif (itemId == FourCC("I08U")) then LUA_VAR_FURBOLGSPIRITDMG = 0.25 LUA_VAR_FURBOLGSPIRITDURATION = 8.0 end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_FURBOLGITEMBOOL,itemTableUlt)
end
function FurbolgAbilsInit(pInt)
Furbolg_Frenzy = CreateTrigger()
Furbolg_SpiritBash = CreateTrigger()
Furbolg_Roar = CreateTrigger()
Furbolg_Barkskin = CreateTrigger()
Furbolg_Fury = CreateTrigger()
Furbolg_Might = CreateTrigger()
LUA_VAR_FURBOLGFRENZYHITS = 0
LUA_VAR_FURBOLGFRENZYTARGET = 0
LUA_VAR_FURBOLGFRENZYTIMER = NewTimer()
LUA_VAR_FURBOLGSPIRITGROUP = CreateGroup()
LUA_VAR_FURBOLGFURYACTIVE = false
LUA_VAR_FURBOLGFURYATTACKS = 0
LUA_VAR_FURBOLGFRENZYBONUSDMG = 0
LUA_VAR_FURBOLGFRENZYMULT = 0.10
LUA_VAR_FURBOLGSPIRITDMG = 0.20
LUA_VAR_FURBOLGSPIRITDURATION = 6.0
local Furbolg_Frenzy_f = function()
LUA_VAR_FURBOLGFRENZYHITS = LUA_VAR_FURBOLGFRENZYHITS + 1
ReleaseTimer(LUA_VAR_FURBOLGFRENZYTIMER)
LUA_VAR_FURBOLGFRENZYTIMER = NewTimer()
TimerStart(LUA_VAR_FURBOLGFRENZYTIMER,3.0,false,function()
LUA_VAR_FURBOLGFRENZYTARGET = 0
LUA_VAR_FURBOLGFRENZYHITS = 0
LUA_VAR_FURBOLGFRENZYBONUSDMG = 0
ReleaseTimer()
end)
-- additional hits checking stored target:
if (LUA_VAR_FURBOLGFRENZYHITS > 1 and LUA_VAR_FURBOLGFRENZYTARGET == udg_DamageEventTarget) then
-- only allow up to 100% bonus damage:
if (LUA_VAR_FURBOLGFRENZYBONUSDMG < 1.0) then
-- starts on 2nd attack, so subtract first (LUA_VAR_FURBOLGFRENZYMULT):
LUA_VAR_FURBOLGFRENZYBONUSDMG = LUA_VAR_FURBOLGFRENZYHITS*LUA_VAR_FURBOLGFRENZYMULT - LUA_VAR_FURBOLGFRENZYMULT
elseif (LUA_VAR_FURBOLGITEMBOOL[8] and LUA_VAR_FURBOLGFRENZYBONUSDMG >= 1.0) then
DamagePackageAddMana(udg_DamageEventSource,2.5,true)
end
UnitDamageTargetBJ(udg_DamageEventSource,udg_DamageEventTarget,udg_DamageEventAmount*LUA_VAR_FURBOLGFRENZYBONUSDMG,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
-- if first hit, start timer and set target:
else
LUA_VAR_FURBOLGFRENZYTARGET = udg_DamageEventTarget
LUA_VAR_FURBOLGFRENZYHITS = 1
LUA_VAR_FURBOLGFRENZYBONUSDMG = 0
end
-- if ult active, do effects:
if (LUA_VAR_FURBOLGFURYACTIVE) then
LUA_VAR_FURBOLGFURYATTACKS = LUA_VAR_FURBOLGFURYATTACKS + 1
if (LUA_VAR_FURBOLGFURYATTACKS > 0 and math.fmod(LUA_VAR_FURBOLGFURYATTACKS,2) == 0) then
Furbolg_CreateLingeringSpirit_f(GetUnitX(udg_DamageEventSource),GetUnitY(udg_DamageEventSource),GetOwningPlayer(udg_DamageEventSource))
end
end
-- item effects:
if (LUA_VAR_FURBOLGITEMBOOL[16]
and UnitHasBuffBJ(udg_DamageEventSource,FourCC('B01O'))
and IsUnitType(udg_DamageEventTarget,UNIT_TYPE_HERO)
and not IsUnitType(udg_DamageEventTarget,UNIT_TYPE_ANCIENT)) then
UnitDamageTargetBJ(udg_DamageEventSource,udg_DamageEventTarget,BlzGetUnitMaxHP(udg_DamageEventSource)*.025,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
if (LUA_VAR_FURBOLGITEMBOOL[6] and UnitHasBuffBJ(udg_DamageEventSource,FourCC('B01P'))) then
DamagePackageDummyTarget(udg_DamageEventSource,'A04B','slow',udg_DamageEventTarget)
end
end
-- create a lingering spirit for the Furbolg at x,y
Furbolg_CreateLingeringSpirit_f = function(x,y,player)
math.randomseed( GetTimeOfDay()*10000000*bj_PI )
local randomN = math.random(0, 100)
local pInt = GetConvertedPlayerId(player)
local dummy = DamagePackageCreateDummy(player, 'n01F', x+randomN, y+randomN, 270.0, LUA_VAR_FURBOLGSPIRITDURATION+0.15)
SetUnitVertexColorBJ(dummy, 75, 75, 100, 40)
SpellPackSetUnitStats(udg_playerHero[pInt],dummy,0,LUA_VAR_FURBOLGSPIRITDMG,nil)
GroupAddUnit(LUA_VAR_FURBOLGSPIRITGROUP,dummy)
TimerStart(NewTimer(),LUA_VAR_FURBOLGSPIRITDURATION,false,function() DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',
GetUnitX(dummy),GetUnitY(dummy))) RemoveUnit(dummy) GroupRemoveUnit(LUA_VAR_FURBOLGSPIRITGROUP,dummy) dummy = nil
if (LUA_VAR_FURBOLGITEMBOOL[4]) then
DamagePackageRestoreHealth(udg_playerHero[pInt],2.0,true,
'Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',udg_playerHero[pInt]) end
ReleaseTimer() end)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',GetUnitX(dummy),GetUnitY(dummy)))
end
local Furbolg_SpiritBash_f = function()
local caster, tarX, tarY, target, casterX, casterY = SpellPackSetupTargetUnitAbil()
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*1.25
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl',tarX,tarY))
UnitDamageTargetBJ(caster,target,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
if (LUA_VAR_FURBOLGITEMBOOL[12]) then
local endloop = 2
-- if forest creature, triple the item bonus:
if (GetOwningPlayer(target) == Player(24)) then
endloop = endloop + 2
end
for i = 1,endloop do
Furbolg_CreateLingeringSpirit_f(casterX,casterY,GetOwningPlayer(caster))
end
else
Furbolg_CreateLingeringSpirit_f(casterX,casterY,GetOwningPlayer(caster))
end
if (LUA_VAR_FURBOLGITEMBOOL[1] and GetWidgetLife(target) < 0.41) then
for i = 1,2 do
Furbolg_CreateLingeringSpirit_f(casterX,casterY,GetOwningPlayer(caster))
end
end
if (LUA_VAR_FURBOLGITEMBOOL[5] and GetUnitLifePercent(caster) < 50.0) then
DamagePackageRestoreHealth(caster,8.0,true,'Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',caster)
end
GroupActionAttackUnit(LUA_VAR_FURBOLGSPIRITGROUP,target)
end
local Furbolg_Roar_f = function()
local caster, casterX, casterY = SpellPackSetupInstantAbil()
Furbolg_CreateLingeringSpirit_f(casterX,casterY,GetOwningPlayer(caster))
DamagePackageDealAOE(caster, casterX, casterY, 600.0, 0.0, false, true, 'A009', 'slow', '', 1.0)
if (LUA_VAR_FURBOLGITEMBOOL[2]) then
DamagePackageDummyTarget(caster,'A045','bloodlust')
end
if (LUA_VAR_FURBOLGITEMBOOL[13]) then
BlzEndUnitAbilityCooldown( caster, FourCC('A06A') )
BlzEndUnitAbilityCooldown( caster, FourCC('A06B') )
end
end
local Furbolg_Barkskin_f = function()
local caster, tarX, tarY, target, casterX, casterY = SpellPackSetupTargetUnitAbil()
Furbolg_CreateLingeringSpirit_f(tarX,tarY,GetOwningPlayer(caster))
if (LUA_VAR_FURBOLGITEMBOOL[7]) then
DamagePackageRestoreHealth(target,10.0,true,'Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl',caster)
end
if (LUA_VAR_FURBOLGITEMBOOL[3] and caster ~= target) then
DamagePackageDummyTarget(caster,'A06B','innerfire')
end
if (LUA_VAR_FURBOLGITEMBOOL[14] and caster ~= target) then
local i = 0
TimerStart(NewTimer(),1.0,true,function()
DamagePackageAddMana(target,2.5,true)
i = i + 1
if (i == 6 or not IsUnitAliveBJ(target)) then
ReleaseTimer()
end
end)
end
GroupActionRemoveDead(LUA_VAR_FURBOLGSPIRITGROUP) -- occasional just-in-case cleansing
end
local Furbolg_Fury_f = function()
DamagePackageDummyTarget(GetTriggerUnit(),'A06F','bloodlust')
LUA_VAR_FURBOLGFURYATTACKS = 0
LUA_VAR_FURBOLGFURYACTIVE = true
TimerStart(NewTimer(),12.0,false,function()
LUA_VAR_FURBOLGFURYACTIVE = false
end)
end
local Furbolg_Might_f = function()
local caster, tarX, tarY, target, casterX, casterY = SpellPackSetupTargetUnitAbil()
local dmg
DestroyEffect(AddSpecialEffect('Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl',tarX,tarY))
for i = 1,3 do
Furbolg_CreateLingeringSpirit_f(casterX,casterY,GetOwningPlayer(caster))
end
BasicKnockback(target, AngleBetweenPointsXY(casterX, casterY, tarX, tarY), 200.0, false, 0.25, nil, nil)
if (LUA_VAR_FURBOLGITEMBOOL[17]) then
DamagePackageDummyTarget(caster,'A06G','thunderbolt',target)
dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*2.5
else
DamagePackageDummyTarget(caster,'A03H','thunderbolt',target)
dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*2.0
end
UnitDamageTargetBJ(caster,target,dmg,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
local frenzy_boolexpr = function() return udg_DamageEventAttackT == udg_ATTACK_TYPE_HERO and udg_DamageEventSource == udg_playerHero[pInt] end
SpellPackCreateDamageTrigger(Furbolg_Frenzy,Furbolg_Frenzy_f,pInt,frenzy_boolexpr,nil)
SpellPackCreateTrigger(Furbolg_SpiritBash,Furbolg_SpiritBash_f,pInt,'A06A')
SpellPackCreateTrigger(Furbolg_Roar,Furbolg_Roar_f,pInt,'A06C')
SpellPackCreateTrigger(Furbolg_Barkskin,Furbolg_Barkskin_f,pInt,'A06B')
SpellPackCreateTrigger(Furbolg_Fury,Furbolg_Fury_f,pInt,'A06E')
SpellPackCreateTrigger(Furbolg_Might,Furbolg_Might_f,pInt,'A06D')
end
-- overlord v1.0
function OverlordInit(unit, i)
OverlordItemsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNFelGuardBlue.blp'
LeaderboardUpdateHeroIcon(i)
udg_overlordUnit = unit
HeroTriggersOverlord()
end
function OverlordItemsInit(pInt)
LUA_VAR_OVERLORDITEMBOOL = {}
-- raw code and descriptions
local itemTable = {
"I024", -- 1 (Q) Seething Wrathstone
"I026", -- 2 (W) Legion Shield Beacon
"I027", -- 3 (E) Overwhelming Gauntlets
"I028", -- 4 (Trait) Oozing Cleaver
"I029", -- 5 (Q) Key of Cauterizing Flame
"I06Z", -- 6 (W) Legion Horn of Command -- not working
"I070", -- 7 (E) Overwhelming Pauldrons
"I071", -- 8 (Trait) Blistering Felstone
"I06V", -- 9 (Q) Key of Condensed Flame
"I06W", -- 10 (W) Cloak of Fel Absorption
"I06X" -- 11 (E) Overwhelming Belt
}
local itemTableUlt = {
"I06Y", -- 12 (Q) Hellfire Trinket
"I073", -- 13 (W) Amalgamated Felstone
"I074", -- 14 (E) Trinket of Overwhelming Power
"I072", -- 15 (Stats) Felforged Plating
"I076", -- 16 (R1) Wyrmtongue Engineering Crew
"I075" -- 17 (R2) Gorefiend's Terror
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I06V")) then IncUnitAbilityLevel(udg_overlordUnit,FourCC("A03T"))
elseif (itemId == FourCC("I06W")) then IncUnitAbilityLevel(udg_overlordUnit,FourCC("A03U"))
elseif (itemId == FourCC("I06X")) then IncUnitAbilityLevel(udg_overlordUnit,FourCC("A03V"))
elseif (itemId == FourCC("I06X")) then HeroAddStatTimer(udg_overlordUnit, 0, 1, 45.0, 12)
elseif (itemId == FourCC("I075")) then LUA_VAR_OVERLORDITEM_I_17 = 0
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,LUA_VAR_OVERLORDITEMBOOL,itemTableUlt)
end
function HeroTriggersOverlord()
Overlord_Cleave = CreateTrigger()
Overlord_Wrath = CreateTrigger()
Overlord_Pact = CreateTrigger()
Overlord_Brutalize = CreateTrigger()
Overlord_Sentinax = CreateTrigger()
Overlord_Terror = CreateTrigger()
LUA_VAR_OVERLORDCLEAVECOUNT = 0
local Overlord_Wrath_f = function()
local i = 0
local ticks = 24 -- number of ticks (duration = ticks * 0.25)
local originLoc = GetSpellTargetLoc()
local x = GetLocationX(originLoc)
local y = GetLocationY(originLoc)
local radius = 150.0
local damageBonus = 0.03
local unit = GetTriggerUnit()
local damage = GetHeroStatBJ(bj_HEROSTAT_STR, unit, true)*.25
local p = GetOwningPlayer(unit)
if (LUA_VAR_OVERLORDITEMBOOL[1]) then
ticks = 32
damage = damage*1.1
end
if (LUA_VAR_OVERLORDITEMBOOL[12]) then
damageBonus = damageBonus + .025
end
TimerStart(NewTimer(),0.25, true, function()
if (ModuloInteger(i,6) == 0) then
local effect = AddSpecialEffect('war3mapImported\\Conflagrate Green.mdx', x, y)
DestroyEffect(effect)
end
i = i + 1
local g = CreateGroup()
GroupEnumUnitsInRange(g, x, y, radius, Condition( function() return
IsUnitEnemy(GetFilterUnit(),p)
and IsUnitAliveBJ(GetFilterUnit())
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
local u = FirstOfGroup(g)
local i2 = 0
repeat
UnitDamageTargetBJ(unit,u,damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
if (IsUnitType(u,UNIT_TYPE_HERO) and not IsUnitType(u,UNIT_TYPE_ANCIENT)) then
damage = damage + (damage*damageBonus)
end
if (LUA_VAR_OVERLORDITEMBOOL[5]) then
DamagePackageRestoreHealth(unit,damage*.15,false,'_',unit)
end
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
i2 = i2 + 1
until (u == nil or i2 == 24) -- prevent infinite loop
end
if (i >= ticks) then
ReleaseTimer()
end
end)
RemoveLocation(originLoc)
--RemoveLocation(loc)
end
local Overlord_Pact_f = function()
local u = GetTriggerUnit()
local t = GetSpellTargetUnit()
DamagePackageDummyTarget(u,'A03U','spiritlink')
if (LUA_VAR_OVERLORDITEMBOOL[2]) then
DamagePackageDummyTarget(u,'A00O','innerfire')
DamagePackageDummyTarget(t,'A00O','innerfire')
end
if (LUA_VAR_OVERLORDITEMBOOL[6]) then
DamagePackageDummyTarget(u,'A042','bloodlust')
DamagePackageDummyTarget(t,'A042','bloodlust')
end
if (LUA_VAR_OVERLORDITEMBOOL[13]) then
local i = 0
TimerStart(NewTimer(i),1.0,true,function()
DamagePackageRestoreHealth(udg_overlordUnit,4,true,'Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl',udg_overlordUnit)
i = i+1
if (i >= 5) then
ReleaseTimer()
end
end)
end
end
local Overlord_Brutalize_f = function()
local u = GetTriggerUnit()
local t = GetSpellTargetUnit()
local loc = GetUnitLoc(u)
local loc2 = GetUnitLoc(t)
local angle = AngleBetweenPoints(loc,loc2)-180.0
local travelDistance = DistanceBetweenPoints(loc,loc2)+200.0
if (IsUnitEnemy(t) and LUA_VAR_OVERLORDITEMBOOL[3]) then
travelDistance = travelDistance + 100
end
local endLoc = PolarProjectionBJ(loc,travelDistance,angle)
local velocity = travelDistance/9.0
local distance = 0.0
local i = 0
local damage = GetHeroStatBJ(bj_HEROSTAT_STR, u, true)*1.25
if (LUA_VAR_OVERLORDITEMBOOL[7]) then
damage = damage*1.25
end
SetUnitPathing(t, false)
SetUnitPathing(u, false)
PauseUnit(u,true)
PauseUnit(t,true)
SetUnitAnimation(u,"spell")
local x = GetLocationX(loc2)
local y = GetLocationY(loc2)
TimerStart(NewTimer(),0.04,true,function()
if (i <= 9 and IsUnitAliveBJ(t) and IsUnitAliveBJ(u)) then
i = i+1
distance = distance + velocity
local pLoc = Location(x,y)
local arrivalLoc = PolarProjectionBJ(pLoc,distance,angle)
SetUnitPositionLoc(t,arrivalLoc)
RemoveLocation(pLoc)
RemoveLocation(arrivalLoc)
else
local x2 = GetUnitX(t)
local y2 = GetUnitY(t)
local effect = AddSpecialEffect('Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl',x2,y2)
if (IsUnitEnemy(t,GetOwningPlayer(u))) then
UnitDamageTargetBJ(u,t,damage,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
if (LUA_VAR_OVERLORDITEMBOOL[3]) then
DamagePackageDummyTarget(u,'A03H','thunderbolt',t)
end
if (LUA_VAR_OVERLORDITEMBOOL[7]) then
if (LUA_VAR_OVERLORDITEMBOOL[3]) then -- if stun item is true, apply the slow after it
TimerStart(NewTimer(),1.0,false,function() DamagePackageDummyTarget(u,'A04B','slow',t) ReleaseTimer() end)
else
DamagePackageDummyTarget(u,'A04B','slow',t)
end
end
if (LUA_VAR_OVERLORDITEMBOOL[14]) then
UnitDamageTargetBJ(u,t,math.floor(BlzGetUnitMaxHP(t)*.1),ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
end
else
DamagePackageDummyTarget(t,'A00O','innerfire')
end
DestroyEffect(effect)
PauseUnit(u,false)
PauseUnit(t,false)
ResetUnitAnimation(u)
SetUnitPathing(t, true)
SetUnitPathing(u, true)
ReleaseTimer()
end
end)
RemoveLocation(loc)
RemoveLocation(loc2)
RemoveLocation(endLoc)
end
local Overlord_Sentinax_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local i = 6
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, caster, true)*1.0
if (LUA_VAR_OVERLORDITEMBOOL[16]) then
i = i + 2
dmg = dmg*1.25
end
local a, x2, y2, r = 0
local radius = 350.0
local effectRadius = 235.0
UnitApplyTimedLifeBJ( i+1, FourCC('BTLF'), CreateUnit(GetOwningPlayer(caster),FourCC('e004'),tarX,tarY,270.0) ) -- visibility dummy
PolledWait(1.0) -- inaccurate wait to allow reaction times
TimerStart(NewTimer(),1.0,true,function()
if (i <= 0) then
ReleaseTimer()
else
for loop = 1,4 do
a = math.random() * 2 * 3.141 -- simplify pi to save on calc time
r = effectRadius * math.sqrt(math.random())
x2 = tarX + (r * math.cos(a))
y2 = tarY + (r * math.sin(a))
DestroyEffect(AddSpecialEffect('Units\\Demon\\Infernal\\InfernalBirth.mdl',x2,y2))
end
DamagePackageDealAOE(caster, tarX, tarY, radius, dmg, false, false, '', '', 'Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl', 1.0)
i = i - 1
end
end)
end
local Overlord_Terror_f = function ()
local g = CreateGroup()
local caster = GetSpellAbilityUnit()
local casterX = GetUnitX(caster)
local casterY = GetUnitY(caster)
if (LUA_VAR_OVERLORDITEMBOOL[17]) then
LUA_VAR_OVERLORDITEM_I_17 = 0
end
GroupEnumUnitsInRange(g, casterX, casterY, 600.0, Condition( function() return
IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(caster))
and IsUnitAliveBJ(GetFilterUnit())
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE))
and (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)) end ) )
GroupUtilsAction(g, function()
local x = GetUnitX(LUA_FILTERUNIT)
local y = GetUnitY(LUA_FILTERUNIT)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl', x, y))
end)
DestroyGroup(g)
end
local Overlord_Cleave_f = function()
local damageMult = 1.25
local requiredAttacks = 3
if (LUA_VAR_OVERLORDITEMBOOL[4]) then
damageMult = 1.88 -- str dmg done
requiredAttacks = 4 -- attacks before cleave activates
end
LUA_VAR_OVERLORDCLEAVECOUNT = LUA_VAR_OVERLORDCLEAVECOUNT + 1 -- add attack count
if (LUA_VAR_OVERLORDCLEAVECOUNT > requiredAttacks) then
LUA_VAR_OVERLORDCLEAVECOUNT = 0 -- reset
local casterX = GetUnitX(udg_DamageEventSource)
local casterY = GetUnitY(udg_DamageEventSource)
local tarX,tarY = PolarProjectionXY(casterX, casterY, 125.00, GetUnitFacing(udg_DamageEventSource))
local dmg = GetHeroStatBJ(bj_HEROSTAT_STR, udg_DamageEventSource, true)*damageMult
local effect = 'Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl'
LuaMissile.create(udg_DamageEventSource, tarX, tarY, 400.0, 0.50, dmg, 100.0, false, false, false,
nil, nil, effect, effect, 30.0, 1.50, nil, nil, nil)
if (LUA_VAR_OVERLORDITEMBOOL[8]) then
DamagePackageRestoreHealth(udg_overlordUnit,8,true)
end
if (LUA_VAR_OVERLORDITEMBOOL[17]) then
LUA_VAR_OVERLORDITEM_I_17 = LUA_VAR_OVERLORDITEM_I_17 + 1
if (LUA_VAR_OVERLORDITEM_I_17 >= 2) then
BlzEndUnitAbilityCooldown( udg_overlordUnit, FourCC('A03W') )
LUA_VAR_OVERLORDITEM_I_17 = 0
end
end
end
end
local playerOwner = GetOwningPlayer(udg_overlordUnit)
TriggerRegisterPlayerUnitEvent(Overlord_Wrath,playerOwner,EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Overlord_Wrath, Filter( function() return GetSpellAbilityId() == FourCC('A03T') end ) )
TriggerAddAction(Overlord_Wrath,Overlord_Wrath_f)
TriggerRegisterPlayerUnitEvent(Overlord_Pact,playerOwner,EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Overlord_Pact, Filter( function() return GetSpellAbilityId() == FourCC('A03U') and GetTriggerUnit() == udg_overlordUnit end ) )
TriggerAddAction(Overlord_Pact,Overlord_Pact_f)
TriggerRegisterPlayerUnitEvent(Overlord_Brutalize,playerOwner,EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Overlord_Brutalize, Filter( function() return GetSpellAbilityId() == FourCC('A03V') end ) )
TriggerAddAction(Overlord_Brutalize,Overlord_Brutalize_f)
TriggerRegisterPlayerUnitEvent(Overlord_Terror,playerOwner,EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Overlord_Terror, Filter( function() return GetSpellAbilityId() == FourCC('A03W') end ) )
TriggerAddAction(Overlord_Terror,Overlord_Terror_f)
TriggerRegisterPlayerUnitEvent(Overlord_Sentinax,playerOwner,EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Overlord_Sentinax, Filter( function() return GetSpellAbilityId() == FourCC('A05Z') end ) )
TriggerAddAction(Overlord_Sentinax,Overlord_Sentinax_f)
TriggerRegisterVariableEvent( Overlord_Cleave, "udg_DamageModifierEvent", EQUAL, 1.00 )
TriggerAddCondition(Overlord_Cleave, Filter( function() return (GetUnitTypeId(udg_DamageEventSource) == FourCC('O016')
and udg_DamageEventAttackT == udg_ATTACK_TYPE_HERO
and udg_DamageEventAmount > 1.00)
end ) )
TriggerAddAction(Overlord_Cleave,Overlord_Cleave_f)
end
function HydraInit(unit, i)
HydraItemsInit(i)
HydraAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNHydra.blp'
LeaderboardUpdateHeroIcon(i)
udg_hydraUnit = unit
EnableTrigger( gg_trg_Hydra_Fire )
EnableTrigger( gg_trg_Hydra_Frost )
EnableTrigger( gg_trg_Hydra_Acid )
EnableTrigger( gg_trg_Hydra_Maw )
EnableTrigger( gg_trg_Hydra_Geyser )
end
function HydraItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I02I", -- 1 (Q) Abyssal Slagstone
"I02J", -- 2 (W) Abyssal Floestone
"I02K", -- 3 (E) Abyssal Venomstone
"I02L", -- 4 (Trait) Deepfathom Lure
"I07J", -- 5 (Q) Vial of Cauterizing Fire
"I07K", -- 6 (W) Vial of Gushing Waters
"I07L", -- 7 (E) Vial of Corrosive Solids
"I02M", -- 8 (Mana) Heart of the Sea
"I07M", -- 9 (Q) Scales of the Salamander
"I07N", -- 10 (W) Scales of the Whiptail
"I07O" -- 11 (E) Scales of the Anole
}
local itemTableUlt = {
"I07P", -- 12 (Q) Gahz'rilla's Fortitude
"I07Q", -- 13 (W) Gahz'rilla's Insight
"I07R", -- 14 (E) Gahz'rilla's Zeal
"I07T", -- 15 (Trait) Aku'mai's Lens
"I07U", -- 16 (R1) Gahz'rilla's Endurance
"I07S" -- 17 (R2) Gahz'rilla's Maw
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00G")) then IncUnitAbilityLevel(udg_hydraUnit,FourCC("A031"))
elseif (itemId == FourCC("I07M")) then IncUnitAbilityLevel(udg_hydraUnit,FourCC("A03D"))
elseif (itemId == FourCC("I07N")) then IncUnitAbilityLevel(udg_hydraUnit,FourCC("A03E"))
elseif (itemId == FourCC("I07O")) then IncUnitAbilityLevel(udg_hydraUnit,FourCC("A03F"))
elseif (itemId == FourCC("I07P")) then HeroAddStatTimer(udg_hydraUnit, 0, 1, 45.0, 12)
elseif (itemId == FourCC("I07Q")) then HeroAddStatTimer(udg_hydraUnit, 2, 1, 45.0, 12)
elseif (itemId == FourCC("I07R")) then HeroAddStatTimer(udg_hydraUnit, 1, 1, 45.0, 10) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_hydraItemBool,itemTableUlt)
end
function HydraAbilsInit(pInt)
Hydra_Song = CreateTrigger()
local Hydra_Song_f = function()
udg_hydraSongBool = true
TimerStart(NewTimer(),8.0,false,function()
udg_hydraSongBool = false
ReleaseTimer()
end)
if (udg_hydraItemBool[16]) then
DamagePackageAddMana(GetTriggerUnit(),30,true)
end
end
TriggerRegisterPlayerUnitEvent(Hydra_Song,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(Hydra_Song, Filter( function() return GetSpellAbilityId() == FourCC('A062') end ) )
TriggerAddAction(Hydra_Song,Hydra_Song_f)
end
function FelOrcWarlockInit(unit, i)
FelOrcWarlockItemsInit(i)
FelOrcWarlockAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNChaosWarlock.blp'
LeaderboardUpdateHeroIcon(i)
EnableTrigger( gg_trg_Fel_Orc_Warlock_Bolt )
EnableTrigger( gg_trg_Fel_Orc_Warlock_Bolt_Listener )
EnableTrigger( gg_trg_Fel_Orc_Warlock_Eradicate )
EnableTrigger( gg_trg_Fel_Orc_Warlock_Seed )
EnableTrigger( gg_trg_Fel_Orc_Warlock_Armor_Listener )
udg_felOrcWarlockUnit = unit
udg_heroCanRegenMana[i] = false
SetUnitManaPercentBJ(unit, 0.0)
end
function FelOrcWarlockItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I02N", -- 1 (Q) Festering Fel Bauble
"I02O", -- 2 (W) Cloak of Consuming Magic
"I02P", -- 3 (E) Unstable Fel Orb
"I02Q", -- 4 (R) Fel Potion
"I02R", -- 5 (Q) Starved Soul Dagger
"I07V", -- 6 (W) Trinket of Dampening
"I07W", -- 7 (E) Memento of Chaos
"I07Y", -- 8 (Trait) Wandering Soul
"I080", -- 9 (Q) Vengeful Aldor Spirit
"I07Z", -- 10 (W) Boon of the Manaseeker
"I07X" -- 11 (E) Memento of Destruction
}
local itemTableUlt = {
"I082", -- 12 (Q) Skull of Gul'dan
"I084", -- 13 (W) Mantle of Gul'dan
"I081", -- 14 (E) Offering of Gul'dan
"I083", -- 15 (Stats) Desecrated Soul
"I086", -- 16 (R1) Blood of Mannoroth
"I085" -- 17 (R2) Heart of Mannoroth
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I02N")) then IncUnitAbilityLevel(udg_felOrcWarlockUnit,FourCC("A02D"))
elseif (itemId == FourCC("I02O")) then IncUnitAbilityLevel(udg_felOrcWarlockUnit,FourCC("A02C"))
elseif (itemId == FourCC("I02R")) then SetPlayerTechResearchedSwap( FourCC('R007'), 1, GetOwningPlayer(udg_felOrcWarlockUnit) )
elseif (itemId == FourCC("I07Y")) then
TimerStart(NewTimer(),12.0,true,function()
if (GetWidgetLife(udg_felOrcWarlockUnit) > .405) then
local rand = math.random(1,100)
DamagePackageAddMana(udg_felOrcWarlockUnit,rand,false)
end
end)
elseif (itemId == FourCC("I083")) then HeroAddStatTimer(udg_felOrcWarlockUnit, 2, 1, 45.0, 10)
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_felOrcWarlockItemBool,itemTableUlt)
end
function FelOrcWarlockAbilsInit(pInt)
FelOrcWarlock_Despoil = CreateTrigger()
local FelOrcWarlock_Despoil_f = function()
local i = 0
local caster = GetTriggerUnit()
local damage = math.floor(BlzGetUnitMaxHP(caster)*.10)
if (udg_felOrcWarlockItemBool[16]) then
damage = math.floor(damage*.70)
end
TimerStart(NewTimer(),1.0,true,function()
i = i + 1
DamagePackageAddMana(caster,75,false)
DamagePackageDealDirect(caster, caster, damage, 0, 0, false, 'Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl')
if ( i >= 6) then
ReleaseTimer()
end
end)
end
TriggerRegisterPlayerUnitEvent(FelOrcWarlock_Despoil,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(FelOrcWarlock_Despoil, Filter( function() return GetSpellAbilityId() == FourCC('A064') end ) )
TriggerAddAction(FelOrcWarlock_Despoil,FelOrcWarlock_Despoil_f)
end
function TuskarrHealerInit(unit, i)
TuskarrHealerItemsInit(i)
TuskarrHealerAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNTuskaarBlack.blp'
LeaderboardUpdateHeroIcon(i)
udg_tuskarrHealerUnit = unit
EnableTrigger( gg_trg_Tuskarr_Healer_Glaciate )
EnableTrigger( gg_trg_Tuskarr_Healer_Spike )
EnableTrigger( gg_trg_Tuskarr_Healer_Spore )
EnableTrigger( gg_trg_Tuskarr_Healer_Riptide )
EnableTrigger( gg_trg_Tuskarr_Healer_Spirit )
end
function TuskarrHealerItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I02D", -- 1 (Q) Sticky Tar
"I02E", -- 2 (W) Explosive Fungus
"I02F", -- 3 (E) Shamanistic Water
"I02G", -- 4 (F) Hyper-Condensed Blizzard
"I02H", -- 5 (Active) Frisky Sprinters
"I077", -- 6 (W) Quick-Bonding Enzymes
"I078", -- 7 (E) Gushing Potion
"I079", -- 8 (Trait) Ice Cooler
"I07A", -- 9 (Q) Enchanted Fishing Line
"I07B", -- 10 (W) Fishscale Armor
"I07C" -- 11 (E) Fishing, for Dummies Pt. 1
}
local itemTableUlt = {
"I07D", -- 12 (Q) Fish Cannon
"I07E", -- 13 (W) Bottomless Fish Net
"I07F", -- 14 (E) Fishing, for Dummies Pt. 2
"I07G", -- 15 (Trait) Cauterizing Ice Pack
"I07I", -- 16 (R1) Northrend Ice Cube
"I07H" -- 17 (R2) Whalebelly's Tusk
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I00G")) then IncUnitAbilityLevel(udg_tuskarrHealerUnit,FourCC("A03M"))
elseif (itemId == FourCC("I07C")) then IncUnitAbilityLevel(udg_tuskarrHealerUnit,FourCC("A03J"))
elseif (itemId == FourCC("I07D")) then IncUnitAbilityLevel(udg_tuskarrHealerUnit,FourCC("A03K"))
elseif (itemId == FourCC("I07E")) then IncUnitAbilityLevel(udg_tuskarrHealerUnit,FourCC("A03L"))
elseif (itemId == FourCC("I07F")) then IncUnitAbilityLevel(udg_tuskarrHealerUnit,FourCC("A03J")) end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_tuskarrHealerItemBool,itemTableUlt)
end
function TuskarrHealerAbilsInit(pInt)
TuskarrHealer_Wall = CreateTrigger()
local TuskarrHealer_Wall_f = function()
local dumX = {}
local dumY = {}
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local facing = AngleBetweenPointsXY(casterX,casterY,tarX,tarY)
local dur = 6.0
local d
if (udg_tuskarrHealerItemBool[16]) then
dur = dur + 3.0
end
dumX[1],dumY[1] = PolarProjectionXY(tarX,tarY,144,facing-90)
dumX[2],dumY[2] = PolarProjectionXY(tarX,tarY,72,facing-90)
dumX[3],dumY[3] = PolarProjectionXY(tarX,tarY,24,facing)
dumX[4],dumY[4] = PolarProjectionXY(tarX,tarY,72,facing+90)
dumX[5],dumY[5] = PolarProjectionXY(tarX,tarY,144,facing+90)
-- clear collision + a min cell of ~16 to account for hero pathing size:
for i = 1,5 do
SpellPackClearArea(dumX[i],dumY[i],48.0)
d = DamagePackageCreateDummy(GetOwningPlayer(caster), 'e005', dumX[i], dumY[i], facing, dur)
end
dumX = nil
dumY = nil
end
TriggerRegisterPlayerUnitEvent(TuskarrHealer_Wall,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(TuskarrHealer_Wall, Filter( function() return GetSpellAbilityId() == FourCC('A061') end ) )
TriggerAddAction(TuskarrHealer_Wall,TuskarrHealer_Wall_f)
end
function EredarWarlockInit(unit, i)
EredarWarlockItemsInit(i)
EredarWarlockAbilsInit(i)
udg_mbHeroIcon[i] = 'ReplaceableTextures\\CommandButtons\\BTNEredarWarlockPurple.blp'
LeaderboardUpdateHeroIcon(i)
udg_eredarWarlockUnit = unit
EnableTrigger( gg_trg_Eredar_Warlock_Summons )
EnableTrigger( gg_trg_Eredar_Warlock_Invasion_Activate )
EnableTrigger( gg_trg_Eredar_Warlock_Invasion_Gate )
EnableTrigger( gg_trg_Eredar_Warlock_Demonic_Pact )
EnableTrigger( gg_trg_Eredar_Warlock_Orb )
TriggerExecute (gg_trg_Eredar_Warlock_Generate_Objectives)
end
function EredarWarlockItemsInit(pInt)
-- raw code and descriptions
local itemTable = {
"I02S", -- 1 (Q) Shawl of the Burning Legion
"I02T", -- 2 (W) Book of Summoning
"I02U", -- 3 (E) Demonic Harness
"I02V", -- 4 (R) Onslaught Scepter
"I02W", -- 5 (Q) Cranium of the Covenant
"I08B", -- 6 (W) Fel Armaments
"I08C", -- 7 (E) Fel-Infused Marrow
"I08A", -- 8 (Trait) Mobile Invasion Rune
"I088", -- 9 (Q) Channeling Orb
"I089", -- 10 (W) Nether Crystal
"I087" -- 11 (E) Summoning Pact
}
local itemTableUlt = {
"I08D", -- 12 (Q) Orb of Ultimate Destruction
"I08E", -- 13 (W) Orb of Ruinous Invasion
"I08F", -- 14 (E) Orb of Absolute Control
"I08H", -- 15 (Trait) Forge Camp Portal Device
"I08I", -- 16 (R1) Libram of the Burning Legion
"I08G" -- 17 (R2) Forge Camp Platemail
}
-- non-itemBool custom effects
local conditionsFunc = function(itemId)
if (itemId == FourCC("I02T")) then udg_eredarWarlockDemonPoolMax = 12
elseif (itemId == FourCC("I088")) then IncUnitAbilityLevel(udg_eredarWarlockUnit,FourCC("A013"))
elseif (itemId == FourCC("I089")) then IncUnitAbilityLevel(udg_eredarWarlockUnit,FourCC("A010"))
elseif (itemId == FourCC("I087")) then IncUnitAbilityLevel(udg_eredarWarlockUnit,FourCC("A011"))
elseif (itemId == FourCC("I08A")) then IncUnitAbilityLevel(udg_eredarWarlockUnit,FourCC("A00Z"))
end
end
ItemShopGenerateShop(pInt,itemTable,conditionsFunc,udg_eredarWarlockItemBool,itemTableUlt)
end
function EredarWarlockAbilsInit(pInt)
EredarWarlock_Mark = CreateTrigger()
local EredarWarlock_Mark_f = function()
local caster, tarX, tarY, casterX, casterY = SpellPackSetupTargetAbil()
local dummy = DamagePackageCreateDummy(GetOwningPlayer(caster),'e00R',casterX,casterY,GetUnitFacing(caster),3.25)
local angle = AngleBetweenPointsXY(casterX,casterY,tarX,tarY)
local moveX,moveY = PolarProjectionXY(casterX,casterY,800.0,angle)
local i = 0
local baseX
local baseY
local newOwner
local enemyOwner
local g = CreateGroup()
local currentX
local currentY
local u
local pColor
SetUnitMoveSpeed(dummy,200.0) -- move 600 units over 3 sec
IssuePointOrder(dummy,"move",moveX,moveY)
if (GetConvertedPlayerId(GetOwningPlayer(caster)) > 5) then
newOwner = Player(13) -- alliance
enemyOwner = Player(12)
baseX = GetRectCenterX(gg_rct_coreHorde)
baseY = GetRectCenterY(gg_rct_coreHorde)
pColor = PLAYER_COLOR_NAVY
else
newOwner = Player(12) -- horde
enemyOwner = Player(13)
baseX = GetRectCenterX(gg_rct_coreAlliance)
baseY = GetRectCenterY(gg_rct_coreAlliance)
pColor = PLAYER_COLOR_MAROON
end
TimerStart(NewTimer(),0.25,true,function()
i = i + 1
currentX = GetUnitX(dummy)
currentY = GetUnitY(dummy)
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl',currentX,currentY))
GroupEnumUnitsInRange(g, currentX, currentY, 200.0, Condition( function() return DamagePackageEnemyFilter(GetFilterUnit(), GetOwningPlayer(caster))
and GetOwningPlayer( GetFilterUnit() ) == enemyOwner
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_SUMMONED)
and not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) end ) )
if (not IsUnitGroupEmptyBJ(g)) then
u = FirstOfGroup(g)
local loop = 0
repeat
loop = loop + 1
DestroyEffect(AddSpecialEffect('Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl',GetUnitX(u),GetUnitY(u)))
SetUnitOwner(u,newOwner)
SetUnitColor(u,pColor)
UnitAddType(u,UNIT_TYPE_SUMMONED)
UnitApplyTimedLifeBJ( 24.0, FourCC('BTLF'), u )
if (udg_eredarWarlockItemBool[16]) then
DamagePackageDummyTarget(u,'A068','innerfire')
end
IssuePointOrder(u,"attack",baseX,baseY)
GroupRemoveUnit(g, u)
u = FirstOfGroup(g)
until (u == nil or loop >= 10)
end
if (i >= 12) then
RemoveUnit(u)
u = nil
DestroyGroup(g)
ReleaseTimer()
end
end)
end
TriggerRegisterPlayerUnitEvent(EredarWarlock_Mark,Player(pInt - 1),EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
TriggerAddCondition(EredarWarlock_Mark, Filter( function() return GetSpellAbilityId() == FourCC('A067') end ) )
TriggerAddAction(EredarWarlock_Mark,EredarWarlock_Mark_f)
end
function EnableDevCommands()
local bool
for i = 2,10 do
if (GetPlayerSlotState(Player(i-1)) == PLAYER_SLOT_STATE_EMPTY) then
bool = true
else
bool = false
break
end
end
if (bool == true) then
udg_zEliteShopRemove = false
EnableTrigger(gg_trg_Dev_AIPrintTesting)
EnableTrigger(gg_trg_Dev_Test_Printing)
EnableTrigger(gg_trg_Dev_Obj_Init)
EnableTrigger(gg_trg_Dev_MUI_Test)
EnableTrigger(gg_trg_Dev_Test_Upgrades)
EnableTrigger(gg_trg_Dev_Disable_FF)
EnableTrigger(gg_trg_Dev_Enable_FF)
EnableTrigger(gg_trg_Dev_Disable_Item_Owners)
EnableTrigger(gg_trg_Dev_Waves)
EnableTrigger(gg_trg_Dev_Invul_Debug)
EnableTrigger(gg_trg_Dev_CDs)
EnableTrigger(gg_trg_Dev_Blink_Item)
EnableTrigger(gg_trg_Dev_Damage_Item)
EnableTrigger(gg_trg_Dev_Regen_Item)
EnableTrigger(gg_trg_Dev_Health)
EnableTrigger(gg_trg_Dev_Spawn)
EnableTrigger(gg_trg_Dev_Spawn_10)
EnableTrigger(gg_trg_Dev_Level_Up)
EnableTrigger(gg_trg_Dev_Level_21)
EnableTrigger(gg_trg_Dev_Exp)
EnableTrigger(gg_trg_Dev_Exp_2)
EnableTrigger(gg_trg_Dev_Level_Me)
EnableTrigger(gg_trg_Dev_Food)
EnableTrigger(gg_trg_Dev_Reveal)
EnableTrigger(gg_trg_Dev_Pause_Alliance)
EnableTrigger(gg_trg_Dev_Unpause_Alliance)
EnableTrigger(gg_trg_Dev_Pause_Horde)
EnableTrigger(gg_trg_Dev_Unpause_Horde)
EnableTrigger(gg_trg_Dev_Night)
EnableTrigger(gg_trg_Dev_Gold)
EnableTrigger(gg_trg_Dev_Lumber)
EnableTrigger(gg_trg_Dev_Day)
EnableTrigger(gg_trg_Dev_Kill_Me)
EnableTrigger(gg_trg_Dev_Level_Abils)
EnableTrigger(gg_trg_Dev_Kill_Horde_Base)
EnableTrigger(gg_trg_Dev_Kill_Alliance_Base)
EnableTrigger(gg_trg_Dev_Share)
EnableTrigger(gg_trg_Dev_MapTest)
EnableTrigger(gg_trg_Dev_Elite)
EnableTrigger(gg_trg_Dev_Clear)
end
end
function GeneratePlayerCommands()
playerCommandTriggers = {}
playerCommandTriggers.panToHero = {}
-- create panToHero
playerCommandTriggers.panToHero = CreateTrigger()
for i = 0,9 do
BlzTriggerRegisterPlayerKeyEvent(playerCommandTriggers.panToHero, Player(i), OSKEY_SPACE, 0, true)
end
TriggerAddAction(playerCommandTriggers.panToHero, function()
local pInt = GetConvertedPlayerId(GetTriggerPlayer())
SetCameraQuickPositionForPlayer(Player(pInt-1),GetUnitX(udg_playerHero[pInt]),GetUnitY(udg_playerHero[pInt]))
PanCameraToTimedForPlayer(Player(pInt-1),GetUnitX(udg_playerHero[pInt]),GetUnitY(udg_playerHero[pInt]),0.00)
-- prevent spam that might cause a desync:
TimerStart(NewTimer(),0.25,false,function() EnableTrigger(playerCommandTriggers.panToHero) ReleaseTimer() end)
DisableTrigger(playerCommandTriggers.panToHero)
end)
TriggerAddCondition(playerCommandTriggers.panToHero,Filter(function()
return udg_playerHero[GetConvertedPlayerId(GetTriggerPlayer())] ~= nil
end ) )
-- create setCameraDistance
playerCommandTriggers.setPlayerCamera = CreateTrigger()
for i = 0,9 do
TriggerRegisterPlayerChatEvent( playerCommandTriggers.setPlayerCamera, Player(i), "-cam", true )
end
TriggerAddAction(playerCommandTriggers.setPlayerCamera, function()
SetCameraFieldForPlayer( GetTriggerPlayer(), CAMERA_FIELD_TARGET_DISTANCE, 1900.00, 0.33 )
end)
TriggerAddCondition(playerCommandTriggers.setPlayerCamera,Filter(function()
return udg_playerHero[GetConvertedPlayerId(GetTriggerPlayer())] ~= nil
end ) )
-- create playTutorial
playerCommandTriggers.playTutorial = CreateTrigger()
LUA_VAR_TUTORIAL_X = {}
LUA_VAR_TUTORIAL_Y = {}
for i = 0,9 do
TriggerRegisterPlayerChatEvent( playerCommandTriggers.playTutorial, Player(i), "-tutorial", true )
if (i < 5) then
LUA_VAR_TUTORIAL_X[i+1] = GetRectCenterX(gg_rct_spawnHeroHorde)
LUA_VAR_TUTORIAL_Y[i+1] = GetRectCenterY(gg_rct_spawnHeroHorde)
else
LUA_VAR_TUTORIAL_X[i+1] = GetRectCenterX(gg_rct_spawnHeroAlliance)
LUA_VAR_TUTORIAL_Y[i+1] = GetRectCenterY(gg_rct_spawnHeroAlliance)
end
end
TriggerAddAction(playerCommandTriggers.playTutorial, function()
TutorialGenerate(GetConvertedPlayerId(GetTriggerPlayer()))
udg_zPlayerInTutorial[GetConvertedPlayerId(GetTriggerPlayer())] = true
end)
TriggerAddCondition(playerCommandTriggers.playTutorial,Filter(function()
local pInt = GetConvertedPlayerId(GetTriggerPlayer())
if ( udg_playerHero[pInt] == nil ) then
DisplayTimedTextToPlayer(Player(pInt-1),0,0,0.75,"Choose a hero before running the tutorial!")
return false
elseif (udg_gameHasStarted == true) then
DisplayTimedTextToPlayer(Player(pInt-1),0,0,0.75,"The game has already started!")
return false
elseif (DistanceBetweenXY(GetUnitX(udg_playerHero[pInt]),GetUnitY(udg_playerHero[pInt]),LUA_VAR_TUTORIAL_X[pInt],LUA_VAR_TUTORIAL_Y[pInt]) > 800.0) then
DisplayTimedTextToPlayer(Player(pInt-1),0,0,0.75,"The tutorial can only be started while near your Portal Stone!")
return false
else
return true
end
end ) )
-- create cancelTutorial
playerCommandTriggers.cancelTutorial = CreateTrigger()
LUA_VAR_TUTORIAL_CANCEL = {}
for i = 0,9 do
LUA_VAR_TUTORIAL_CANCEL[i+1] = false
TriggerRegisterPlayerEventEndCinematic( playerCommandTriggers.cancelTutorial, Player(i) )
end
TriggerAddAction(playerCommandTriggers.cancelTutorial, function()
LUA_VAR_TUTORIAL_CANCEL[GetConvertedPlayerId(GetTriggerPlayer())] = true
DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,4.5,"Canceling Tutorial...")
end)
TriggerAddCondition(playerCommandTriggers.cancelTutorial,Filter(function()
return udg_zPlayerInTutorial[GetConvertedPlayerId(GetTriggerPlayer())] == true end ) )
end
function TutorialGenerate(pInt)
IssueImmediateOrder(udg_playerHero[pInt],"holdposition")
local force = GetPlayersMatching(Filter(function() return GetFilterPlayer() == Player(pInt-1) end ) )
local player = Player(pInt-1)
local data = {}
-- initialize horde details:
if (pInt < 6) then
data.panTo = {
{ x = GetUnitX(udg_playerShop[pInt]), y = GetUnitY(udg_playerShop[pInt]) }, -- item shop
{ x = -10200.0, y = -10250.0 }, -- portal stone
{ x = GetUnitX(gg_unit_o00F_0142), y = GetUnitY(gg_unit_o00F_0142) }, -- flight master
{ x = GetUnitX(gg_unit_n002_0069), y = GetUnitY(gg_unit_n002_0069) }, -- enemy base example
{ x = GetRectCenterX(gg_rct_creepsWestBarrens1), y = GetRectCenterY(gg_rct_creepsWestBarrens1) }, -- forest camp
{ x = GetRectCenterX(gg_rct_objectiveSpawnRiver1), y = GetRectCenterY(gg_rct_objectiveSpawnRiver1) }, -- objective
{ x = GetRectCenterX(gg_rct_coreAlliance), y = GetRectCenterY(gg_rct_coreAlliance) } -- enemy base
}
--alliance:
else
data.panTo = {
{ x = GetUnitX(udg_playerShop[pInt]), y = GetUnitY(udg_playerShop[pInt]) },
{ x = 9775, y = 10200.0 },
{ x = GetUnitX(gg_unit_h008_0140), y = GetUnitY(gg_unit_h008_0140) },
{ x = GetUnitX(gg_unit_o004_0066), y = GetUnitY(gg_unit_o004_0066) },
{ x = GetRectCenterX(gg_rct_creepsEastForest1), y = GetRectCenterY(gg_rct_creepsEastForest1) },
{ x = GetRectCenterX(gg_rct_objectiveSpawnRiver2), y = GetRectCenterY(gg_rct_objectiveSpawnRiver2) },
{ x = GetRectCenterX(gg_rct_coreHorde), y = GetRectCenterY(gg_rct_coreHorde) }
}
end
data.displayText = {
[1] = "Earn |cffffc800100 gold|r every 2 levels. Buy items even when not at base (|cffffc800<F2>|r, |cffffc800<F3>|r).",
[2] = "Return to base quickly with your |cffffc800Scroll of Town Portal (T)|r.",
[3] = "Use |cffffc800Flight Masters|r to quickly fly to lanes.",
[4] = "Destroy enemy bases to capture them, extending your |cffffc800Flight Master|r range.",
[5] = "Defeat |cffffc800Forest Camps|r to add them to your faction's minion waves.",
[6] = "|cffffc800Objectives|r will spawn periodically. Be the victor for powerful rewards. ",
[7] = "Push the enemy's base and destroy their |cffffc800core|r to win!"
}
CinematicModeExBJ(true, force, 0.00)
local i = 0
local index = 0
ClearTextMessagesBJ(force)
DisplayTimedTextToPlayer(player,0,0,8.5,"Welcome to the tutorial! (To cancel, press |cffffc800<ESC>|r)")
DisplayTimedTextToPlayer(player,0,0,8.5," ")
DisplayTimedTextToPlayer(player,0,0,8.5,"In Champions of Azeroth, both teams have a |cffffc800shared hero level|r.")
VolumeGroupResetBJ()
TutorialPlaySound(player)
TimerStart(NewTimer(),1.63,true,function()
i = i + 1
if i == 3 then SetCameraFieldForPlayer( player, CAMERA_FIELD_TARGET_DISTANCE, 1000.00, 0.33 ) end
if math.fmod(i,3) == 0 and i < 22 then
index = index + 1
ClearTextMessagesBJ(force)
PanCameraToTimedForPlayer(player,data.panTo[index].x,data.panTo[index].y,0.2)
DisplayTimedTextToPlayer(player,0,0,8.5,data.displayText[index])
TutorialPlaySound(player)
end
if i >= 24 or LUA_VAR_TUTORIAL_CANCEL[pInt] == true then
PanCameraToTimedForPlayer(player,GetUnitX(udg_playerHero[pInt]),GetUnitY(udg_playerHero[pInt]),0.2)
SetCameraFieldForPlayer( player, CAMERA_FIELD_TARGET_DISTANCE, 1650.00, 0.33 )
ClearTextMessagesBJ(force)
if not LUA_VAR_TUTORIAL_CANCEL[pInt] then
DisplayTimedTextToPlayer(player,0,0,7.5,"First step? Head to a lane and start gathering EXP!")
DisplayTimedTextToPlayer(player,0,0,7.5,"|cffffc800Forest Creatures|r spawn a few minutes after the battle begins.")
DisplayTimedTextToPlayer(player,0,0,7.5," ")
DisplayTimedTextToPlayer(player,0,0,7.5,"See |cffffc800Quests|r for more details. Have fun!")
end
TutorialPlaySound(player)
CinematicModeExBJ(false, force, 0.00)
DestroyForce(force)
udg_zPlayerInTutorial[pInt] = false
LUA_VAR_TUTORIAL_CANCEL[pInt] = false
data = nil
ReleaseTimer()
end
end)
end
function TutorialPlaySound(p)
if GetLocalPlayer() == p then
StartSound(udg_gameSoundTutorialHint)
StopSound(udg_gameSoundTutorialHint,false,2.5)
end
end
--[[--------------------------------------------------------]]
-- OVERVIEW OF MATRIX USAGE:
-- top: 0 1 [2] {3} 4 5
-- mid: 0 1 [2] {3} 4 5
-- bot: 0 1 [2] {3} 4 5
-- [] = furthest horde FP point
-- {} = furthest alliance FP point
--
-- when a base is destroyed, we fetch the assigned base in each given lane with fp__factionMapHorde/Alliance
-- changing the delta accordingly, e.g. top base for horde destroyed now changes the top mapping via increment/decrement
-- for each respective faction:
-- top: 0 [1] {2} 3 4 5
--[[--------------------------------------------------------]]
function FlightPathDebugHelper(pInt)
TimerStart(NewTimer(pInt),6.0,false,function()
if (IsUnitAliveBJ(udg_flightPathOwner[pInt])) then
local unit = udg_playerHero[pInt]
ShowUnit(unit, true)
SetUnitInvulnerable(unit, false)
PauseUnit(unit, false)
SetUnitVertexColorBJ(unit,100,100,100,0)
RemoveUnit(udg_flightPathOwner[pInt])
unit = nil
end
ReleaseTimer()
end)
end
function FlightPathListenerGenerate()
LUA_VAR_FP_OFFSET = 190.0
udg_flightPathFurthestHorde[1] = Location(GetRectCenterX(gg_rct_laneHordeBase4) + LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneHordeBase4) - LUA_VAR_FP_OFFSET)
udg_flightPathFurthestHorde[2] = Location(GetRectCenterX(gg_rct_laneHordeBase5) + LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneHordeBase5) - LUA_VAR_FP_OFFSET)
udg_flightPathFurthestHorde[3] = Location(GetRectCenterX(gg_rct_laneHordeBase6) + LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneHordeBase6) - LUA_VAR_FP_OFFSET)
udg_flightPathFurthestAlliance[1] = Location(GetRectCenterX(gg_rct_laneAllianceBase4) - LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneAllianceBase4) + LUA_VAR_FP_OFFSET)
udg_flightPathFurthestAlliance[2] = Location(GetRectCenterX(gg_rct_laneAllianceBase5) - LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneAllianceBase5) + LUA_VAR_FP_OFFSET)
udg_flightPathFurthestAlliance[3] = Location(GetRectCenterX(gg_rct_laneAllianceBase6) - LUA_VAR_FP_OFFSET, GetRectCenterY(gg_rct_laneAllianceBase6) + LUA_VAR_FP_OFFSET)
fp__trigger = {}
fp__regions = {
-- top
[1] = gg_rct_laneHordeBaseMain1,
[2] = gg_rct_laneHordeBase1,
[3] = gg_rct_laneHordeBase4,
[4] = gg_rct_laneAllianceBase4,
[5] = gg_rct_laneAllianceBase1,
[6] = gg_rct_laneAllianceBaseMain1,
-- mid
[7] = gg_rct_laneHordeBaseMain2,
[8] = gg_rct_laneHordeBase2,
[9] = gg_rct_laneHordeBase5,
[10] = gg_rct_laneAllianceBase5,
[11] = gg_rct_laneAllianceBase2,
[12] = gg_rct_laneAllianceBaseMain2,
-- bot
[13] = gg_rct_laneHordeBaseMain3,
[14] = gg_rct_laneHordeBase3,
[15] = gg_rct_laneHordeBase6,
[16] = gg_rct_laneAllianceBase6,
[17] = gg_rct_laneAllianceBase3,
[18] = gg_rct_laneAllianceBaseMain3
}
fp__regions__top = {
[0] = gg_rct_laneHordeBaseMain1,
[1] = gg_rct_laneHordeBase1,
[2] = gg_rct_laneHordeBase4,
[3] = gg_rct_laneAllianceBase4,
[4] = gg_rct_laneAllianceBase1,
[5] = gg_rct_laneAllianceBaseMain1
}
fp__regions__mid = {
[0] = gg_rct_laneHordeBaseMain2,
[1] = gg_rct_laneHordeBase2,
[2] = gg_rct_laneHordeBase5,
[3] = gg_rct_laneAllianceBase5,
[4] = gg_rct_laneAllianceBase2,
[5] = gg_rct_laneAllianceBaseMain2
}
fp__regions__bot = {
[0] = gg_rct_laneHordeBaseMain3,
[1] = gg_rct_laneHordeBase3,
[2] = gg_rct_laneHordeBase6,
[3] = gg_rct_laneAllianceBase6,
[4] = gg_rct_laneAllianceBase3,
[5] = gg_rct_laneAllianceBaseMain3
}
fp__PointMapTopX = {
[0] = GetRectCenterX(gg_rct_laneHordeBaseMain1),
[1] = GetRectCenterX(gg_rct_laneHordeBase1),
[2] = GetRectCenterX(gg_rct_laneHordeBase4),
[3] = GetRectCenterX(gg_rct_laneAllianceBase4),
[4] = GetRectCenterX(gg_rct_laneAllianceBase1),
[5] = GetRectCenterX(gg_rct_laneAllianceBaseMain1)
}
fp__PointMapMidX = {
[0] = GetRectCenterX(gg_rct_laneHordeBaseMain2),
[1] = GetRectCenterX(gg_rct_laneHordeBase2),
[2] = GetRectCenterX(gg_rct_laneHordeBase5),
[3] = GetRectCenterX(gg_rct_laneAllianceBase5),
[4] = GetRectCenterX(gg_rct_laneAllianceBase2),
[5] = GetRectCenterX(gg_rct_laneAllianceBaseMain2)
}
fp__PointMapBotX = {
[0] = GetRectCenterX(gg_rct_laneHordeBaseMain3),
[1] = GetRectCenterX(gg_rct_laneHordeBase3),
[2] = GetRectCenterX(gg_rct_laneHordeBase6),
[3] = GetRectCenterX(gg_rct_laneAllianceBase6),
[4] = GetRectCenterX(gg_rct_laneAllianceBase3),
[5] = GetRectCenterX(gg_rct_laneAllianceBaseMain3)
}
fp__PointMapTopY = {
[0] = GetRectCenterY(gg_rct_laneHordeBaseMain1),
[1] = GetRectCenterY(gg_rct_laneHordeBase1),
[2] = GetRectCenterY(gg_rct_laneHordeBase4),
[3] = GetRectCenterY(gg_rct_laneAllianceBase4),
[4] = GetRectCenterY(gg_rct_laneAllianceBase1),
[5] = GetRectCenterY(gg_rct_laneAllianceBaseMain1)
}
fp__PointMapMidY = {
[0] = GetRectCenterY(gg_rct_laneHordeBaseMain2),
[1] = GetRectCenterY(gg_rct_laneHordeBase2),
[2] = GetRectCenterY(gg_rct_laneHordeBase5),
[3] = GetRectCenterY(gg_rct_laneAllianceBase5),
[4] = GetRectCenterY(gg_rct_laneAllianceBase2),
[5] = GetRectCenterY(gg_rct_laneAllianceBaseMain2)
}
fp__PointMapBotY = {
[0] = GetRectCenterY(gg_rct_laneHordeBaseMain3),
[1] = GetRectCenterY(gg_rct_laneHordeBase3),
[2] = GetRectCenterY(gg_rct_laneHordeBase6),
[3] = GetRectCenterY(gg_rct_laneAllianceBase6),
[4] = GetRectCenterY(gg_rct_laneAllianceBase3),
[5] = GetRectCenterY(gg_rct_laneAllianceBaseMain3)
}
fp__factionMapHorde = {
[1] = 2,
[2] = 2,
[3] = 2
}
fp__factionMapAlliance = {
[1] = 3,
[2] = 3,
[3] = 3
}
-- units eligible to create an outpost for:
local boolExpr = Filter( function()
return IsUnitType(GetTriggerUnit(),UNIT_TYPE_STRUCTURE)
and ( GetUnitTypeId(GetTriggerUnit()) == FourCC('n002')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('h002')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('n001')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('o002')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('o003')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('o004')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('h00E')
or GetUnitTypeId(GetTriggerUnit()) == FourCC('o018') )
end )
-- listener funcs
local fp__func = function()
local laneInt
local unitId
local capturingPlayer
local u = GetTriggerUnit()
local x
local y
local pInt = GetConvertedPlayerId(GetOwningPlayer(u))
-- optimize max loop size by seeing which regions to check
if (pInt == 13) then -- alliance destroyed horde
unitId = FourCC('h00E') -- outpost to build
capturingPlayer = Player(13)
else -- horde destroyed alliance
unitId = FourCC('o018')
capturingPlayer = Player(12)
end
-- find out where base was destroyed
for i = 1,18 do
x = GetRectCenterX(fp__regions[i])
y = GetRectCenterY(fp__regions[i])
if (IsUnitInRangeXY(u, x, y, 300.0)) then
x = GetRectCenterX(fp__regions[i])
y = GetRectCenterY(fp__regions[i])
if (i < 7) then -- top
laneInt = 1
elseif (i > 6 and i < 13) then -- mid
laneInt = 2
elseif (i > 12 and i < 19) then -- bot
laneInt = 3
end
break
else
x = nil
y = nil
end
end
FlightPathUpdate(laneInt, pInt)
RemoveUnit(u)
SpellPackClearArea(x,y,335.0)
local u2 = CreateUnit(capturingPlayer, unitId, x, y, 270.0)
BlzSetUnitMaxHP(u2, math.floor(BlzGetUnitMaxHP(u2) * udg_objRampReal)) -- make bases stronger as game progresses to control for game length
local dmg = math.floor(BlzGetUnitBaseDamage(u2, 0) * 1.33)
BlzSetUnitBaseDamage(u2, dmg, 0)
SetUnitLifePercentBJ(u2, 100.0)
PauseUnit(u2, true)
-- queue build eye candy and 6 sec delay
SetUnitAnimation(u2, "birth")
SetUnitTimeScalePercent (u2, 500.0)
TimerStart(NewTimer(u2),6.0,false,function()
if (IsUnitAliveBJ(u2)) then
PauseUnit(u2, false)
SetUnitTimeScalePercent (u2, 100.0)
SetUnitAnimation(u2, "stand")
ResetUnitAnimation(u2)
end
u2 = nil
ReleaseTimer()
end)
if (udg_aiIsEnabled and udg_objInProgress) then -- if obj in progress and ai is active, update obj engagement statuses
DAI__StateObjectiveIsActive(
GetRectCenterX(gg_rct_objectiveSpawnRiver1),
GetRectCenterY(gg_rct_objectiveSpawnRiver1),
GetRectCenterX(gg_rct_objectiveSpawnRiver2),
GetRectCenterY(gg_rct_objectiveSpawnRiver2) )
end
FlightPathBaseInvul(laneInt, pInt)
DestroyEffect(AddSpecialEffect('Objects\\Spawnmodels\\Human\\HCancelDeath\\HCancelDeath.mdl',x,y))
u = nil
x = nil
y = nil
laneInt = nil
unitId = nil
capturingPlayer = nil
end
for i = 1,2 do
fp__trigger[i] = CreateTrigger()
-- alliance destroys horde base listeners
TriggerRegisterPlayerUnitEvent(fp__trigger[i], Player(11 + i), EVENT_PLAYER_UNIT_DEATH, nil)
TriggerAddCondition(fp__trigger[i], boolExpr)
TriggerAddAction(fp__trigger[i], fp__func)
end
end
-- @lane = lane to update
-- @pInt = faction player number that lost a base; 13 or 14
function FlightPathUpdate(laneInt, pInt)
local newX
local newY
if (pInt == 14) then -- alliance base destroyed, increment lane towards alliance base
fp__factionMapHorde[laneInt] = fp__factionMapHorde[laneInt] + 1
fp__factionMapAlliance[laneInt] = fp__factionMapAlliance[laneInt] + 1
else -- horde base destroyed, decrement laneInt towards horde base
fp__factionMapHorde[laneInt] = fp__factionMapHorde[laneInt] - 1
fp__factionMapAlliance[laneInt] = fp__factionMapAlliance[laneInt] - 1
end
-- update lane locations
if (fp__factionMapHorde[laneInt] ~= -1) then -- if not all bases in lane are destroyed
if (laneInt == 1) then -- top
newX = fp__PointMapTopX[fp__factionMapHorde[laneInt]] + LUA_VAR_FP_OFFSET
newY = fp__PointMapTopY[fp__factionMapHorde[laneInt]] - LUA_VAR_FP_OFFSET
elseif (laneInt == 2) then -- mid
newX = fp__PointMapMidX[fp__factionMapHorde[laneInt]] + LUA_VAR_FP_OFFSET
newY = fp__PointMapMidY[fp__factionMapHorde[laneInt]] - LUA_VAR_FP_OFFSET
elseif (laneInt == 3) then -- bot
newX = fp__PointMapBotX[fp__factionMapHorde[laneInt]] + LUA_VAR_FP_OFFSET
newY = fp__PointMapBotY[fp__factionMapHorde[laneInt]] - LUA_VAR_FP_OFFSET
end
else
newX = GetRectCenterX(gg_rct_flightPathLastHorde)
newY = GetRectCenterY(gg_rct_flightPathLastHorde)
end
RemoveLocation(udg_flightPathFurthestHorde[laneInt])
udg_flightPathFurthestHorde[laneInt] = Location(newX,newY)
if (fp__factionMapAlliance[laneInt] ~= 6) then
if (laneInt == 1) then
newX = fp__PointMapTopX[fp__factionMapAlliance[laneInt]] - LUA_VAR_FP_OFFSET
newY = fp__PointMapTopY[fp__factionMapAlliance[laneInt]] + LUA_VAR_FP_OFFSET
elseif (laneInt == 2) then
newX = fp__PointMapMidX[fp__factionMapAlliance[laneInt]] - LUA_VAR_FP_OFFSET
newY = fp__PointMapMidY[fp__factionMapAlliance[laneInt]] + LUA_VAR_FP_OFFSET
elseif (laneInt == 3) then
newX = fp__PointMapBotX[fp__factionMapAlliance[laneInt]] - LUA_VAR_FP_OFFSET
newY = fp__PointMapBotY[fp__factionMapAlliance[laneInt]] + LUA_VAR_FP_OFFSET
end
else
newX = GetRectCenterX(gg_rct_flightPathLastAlliance)
newY = GetRectCenterY(gg_rct_flightPathLastAlliance)
end
RemoveLocation(udg_flightPathFurthestAlliance[laneInt])
udg_flightPathFurthestAlliance[laneInt] = Location(newX,newY)
end
-- search up and down lanes for next bases and make invulnerable or vulnerable
-- @laneInt = search this lane
-- @pInt = base destroyed for
function FlightPathBaseInvul(laneInt, pInt)
local g = CreateGroup()
local g2 = CreateGroup()
local direction = 1
local x
local y
local u
local u2
local t
local position = 0
local oppInt = 13
if (laneInt == 1) then -- fp__regions table offset
t = fp__regions__top
elseif (laneInt == 2) then -- fp__regions table offset
t = fp__regions__mid
elseif (laneInt == 3) then
t = fp__regions__bot
end
if (pInt == 13) then -- find where in the matrix to search for
oppInt = 14
direction = -direction
position = fp__factionMapHorde[laneInt] + 1 -- get the original position of the dying structure not the updated one
else
position = fp__factionMapAlliance[laneInt] - 1 -- get the original position of the dying structure not the updated one
end
-- make next base vulnerable for faction that lost a base
if (position + direction >= 0 and position + direction <= 5) then -- do nothing if outside of matrix limits
x = GetRectCenterX(t[position + direction])
y = GetRectCenterY(t[position + direction])
GroupEnumUnitsInRange(g, x, y, 50.0, Condition( function() return IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
and GetOwningPlayer(GetFilterUnit()) == Player(pInt - 1) end ) )
u = FirstOfGroup(g)
if (u ~= nil) then
SetUnitInvulnerable(u, false)
end
end
direction = -direction
-- make next base invulnerable for faction that gained a base
if (position + direction >= 0 and position + direction <= 5) then -- do nothing if outside of matrix limits
x = GetRectCenterX(t[position + direction])
y = GetRectCenterY(t[position + direction])
GroupEnumUnitsInRange(g2, x, y, 50.0, Condition( function() return IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
and GetOwningPlayer(GetFilterUnit()) == Player(oppInt - 1) end ) )
u2 = FirstOfGroup(g2)
if (u2 ~= nil) then
SetUnitInvulnerable(u2, true)
end
GroupRemoveUnit(g2,u2)
end
u = nil
u2 = nil
t = nil
DestroyGroup(g)
DestroyGroup(g2)
end