├── .gitignore ├── LICENSE ├── README.md └── game └── scripts ├── npc ├── heroes │ ├── antimage.kv │ ├── meepo.kv │ ├── skywrath_mage.kv │ ├── sniper.kv │ ├── tusk.kv │ └── vengefulspirit.kv ├── npc_abilities_custom.txt └── npc_heroes_custom.txt └── vscripts └── heroes ├── antimage ├── antimage_blink.lua ├── antimage_mana_break.lua ├── antimage_mana_void.lua └── antimage_spell_shield.lua ├── meepo ├── divided_we_stand.lua ├── divided_we_stand.ts ├── earthbind.lua ├── earthbind.ts ├── geostrike.lua ├── geostrike.ts ├── poof.lua └── poof.ts ├── skywrath_mage ├── skywrath_mage_arcane_bolt.lua ├── skywrath_mage_arcane_seal.lua ├── skywrath_mage_concussive_shot.lua ├── skywrath_mage_mystic_flare.lua └── skywrath_mage_target_finder.lua ├── sniper ├── assassinate.lua ├── headshot.lua ├── shrapnel.lua └── take_aim.lua ├── tusk ├── frozen_sigil.lua ├── ice_shards.lua ├── snowball.lua ├── walrus_kick.lua └── walrus_punch.lua ├── util └── base_ability_charges.lua └── vengefulspirit ├── command_aura.lua ├── magic_missle.lua ├── nether_swap.lua └── wave_of_terror.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # Editors 46 | .idea/* 47 | .vscode/* 48 | 49 | #Dota 2 stuff 50 | *.bin 51 | *.vcss_c 52 | *.vxml_c 53 | *.vtex_c 54 | *.vjs_c 55 | *.vpcf_c 56 | *.vmat_c 57 | *.vpk 58 | *.vmdl_c 59 | *.vmesh_c 60 | *.vsndevts_c 61 | *.vsnd_c 62 | panorama_debugger.cfg 63 | game/scripts/vscripts/addon_game_mode.lua 64 | game/scripts/vscripts/dota-std.d.ts 65 | game/scripts/vscripts/dota-modifier-properties.d.ts 66 | game/scripts/vscripts/dota-gameevents.d.ts 67 | game/scripts/vscripts/dota-enums.d.ts 68 | game/scripts/vscripts/dota-api.d.ts 69 | game/scripts/vscripts/tsconfig.json 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ModDota 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AbilityLuaSpellLibrary 2 | A community effort to recreate DotA 2 spells using the custom game lua API. 3 | 4 | Contributions welcome, just make a pull request! 5 | 6 | The successor to the Spell Library by Pizzalol 7 | 8 | ## Style guidelines 9 | Indentation should be 4 spaces. 10 | 11 | Lua ability file should have the following organization: 12 | ``` 13 | LinkLuaModifiers 14 | 15 | Modifier code 16 | 17 | Ability code 18 | ``` 19 | 20 | ## Thanks to our contributors 21 | - [SinZ](https://github.com/SinZ163) 22 | - [Pongping](https://github.com/Wouterz90) 23 | -------------------------------------------------------------------------------- /game/scripts/npc/heroes/antimage.kv: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary spells for Anti Mage /// 3 | /// /// 4 | /// Last Update: 7.07c /// 5 | /// /// 6 | 7 | "DOTAAbilities" 8 | { 9 | //================================================================================================================= 10 | // Ability: Antimage Mana Break 11 | //================================================================================================================= 12 | "lua_antimage_mana_break" 13 | { 14 | // General 15 | //------------------------------------------------------------------------------------------------------------- 16 | "BaseClass" "ability_lua" 17 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 18 | "AbilityUnitDamageType" "DAMAGE_TYPE_PHYSICAL" 19 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 20 | "AbilitySound" "Hero_Antimage.ManaBreak" 21 | "ScriptFile" "heroes/antimage/antimage_mana_break" 22 | 23 | // Special 24 | //------------------------------------------------------------------------------------------------------------- 25 | "AbilitySpecial" 26 | { 27 | "01" 28 | { 29 | "var_type" "FIELD_FLOAT" 30 | "damage_per_burn" "0.5" 31 | } 32 | "02" 33 | { 34 | "var_type" "FIELD_INTEGER" 35 | "mana_per_hit" "28 40 52 64" 36 | } 37 | } 38 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1" 39 | } 40 | 41 | //================================================================================================================= 42 | // Ability: Antimage Blink 43 | //================================================================================================================= 44 | "lua_antimage_blink" 45 | { 46 | // General 47 | //------------------------------------------------------------------------------------------------------------- 48 | "BaseClass" "ability_lua" 49 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES" 50 | "ScriptFile" "heroes/antimage/antimage_blink" 51 | 52 | // Casting 53 | //------------------------------------------------------------------------------------------------------------- 54 | "AbilityCastPoint" "0.4 0.4 0.4 0.4" 55 | 56 | // Time 57 | //------------------------------------------------------------------------------------------------------------- 58 | "AbilityCooldown" "15 12 9 6" 59 | 60 | // Cost 61 | //------------------------------------------------------------------------------------------------------------- 62 | "AbilityManaCost" "60" 63 | 64 | "AbilitySound" "Hero_Antimage.Blink_out" 65 | 66 | // Special 67 | //------------------------------------------------------------------------------------------------------------- 68 | "AbilitySpecial" 69 | { 70 | "01" 71 | { 72 | "var_type" "FIELD_INTEGER" 73 | "blink_range" "925 1000 1075 1150" 74 | "LinkedSpecialBonus" "special_bonus_unique_antimage_3" 75 | } 76 | } 77 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_2" 78 | } 79 | 80 | //================================================================================================================= 81 | // Ability: Antimage Spell Shield 82 | //================================================================================================================= 83 | "lua_antimage_spell_shield" 84 | { 85 | // General 86 | //------------------------------------------------------------------------------------------------------------- 87 | "BaseClass" "ability_lua" 88 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 89 | "ScriptFile" "heroes/antimage/antimage_spell_shield" 90 | 91 | "HasScepterUpgrade" "1" 92 | 93 | // Special 94 | //------------------------------------------------------------------------------------------------------------- 95 | "AbilitySpecial" 96 | { 97 | "01" 98 | { 99 | "var_type" "FIELD_INTEGER" 100 | "spell_shield_resistance" "20 30 40 50" 101 | "LinkedSpecialBonus" "special_bonus_unique_antimage_4" 102 | } 103 | "02" 104 | { 105 | "var_type" "FIELD_INTEGER" 106 | "scepter_cooldown" "12" 107 | } 108 | } 109 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_3" 110 | } 111 | 112 | 113 | //================================================================================================================= 114 | // Ability: Antimage Mana Void 115 | //================================================================================================================= 116 | "lua_antimage_mana_void" 117 | { 118 | // General 119 | //------------------------------------------------------------------------------------------------------------- 120 | "BaseClass" "ability_lua" 121 | "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" 122 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET | DOTA_ABILITY_BEHAVIOR_AOE" 123 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY" 124 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" 125 | "AbilityUnitTargetFlags" "DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES" 126 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_YES" 127 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 128 | "FightRecapLevel" "2" 129 | "AbilitySound" "Hero_Antimage.ManaVoid" 130 | "ScriptFile" "heroes/antimage/antimage_mana_void" 131 | 132 | // Casting 133 | //------------------------------------------------------------------------------------------------------------- 134 | "AbilityCastRange" "600" 135 | "AbilityCastPoint" "0.3 0.3 0.3 0.3" 136 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_4" 137 | 138 | // Time 139 | //------------------------------------------------------------------------------------------------------------- 140 | "AbilityCooldown" "70.0 70.0 70.0" 141 | 142 | // Cost 143 | //------------------------------------------------------------------------------------------------------------- 144 | "AbilityManaCost" "125 200 275" 145 | 146 | // Stats 147 | //------------------------------------------------------------------------------------------------------------- 148 | "AbilityModifierSupportValue" "0.0" // damage only 149 | 150 | // Special 151 | //------------------------------------------------------------------------------------------------------------- 152 | "AbilitySpecial" 153 | { 154 | "01" 155 | { 156 | "var_type" "FIELD_FLOAT" 157 | "mana_void_damage_per_mana" "0.6 0.85 1.1" 158 | } 159 | 160 | "02" 161 | { 162 | "var_type" "FIELD_FLOAT" 163 | "mana_void_ministun" "0.3" 164 | } 165 | 166 | "03" 167 | { 168 | "var_type" "FIELD_INTEGER" 169 | "mana_void_aoe_radius" "500" 170 | } 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /game/scripts/npc/heroes/meepo.kv: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary spells for Meepo /// 3 | /// /// 4 | /// Last Update: 7.07c /// 5 | /// /// 6 | 7 | "DOTAAbilities" 8 | { 9 | //================================================================================================================= 10 | // Meepo: Earthbind 11 | //================================================================================================================= 12 | "meepo_earthbind_lua" 13 | { 14 | // General 15 | //------------------------------------------------------------------------------------------------------------- 16 | "BaseClass" "ability_lua" 17 | "ScriptFile" "heroes/meepo/earthbind.lua" 18 | "AbilityTextureName" "meepo_earthbind" 19 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_AOE | DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING" 20 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 21 | "SpellDispellableType" "SPELL_DISPELLABLE_YES" 22 | "FightRecapLevel" "1" 23 | "AbilitySound" "Hero_Meepo.Earthbind.Cast" 24 | 25 | 26 | // Casting 27 | //------------------------------------------------------------------------------------------------------------- 28 | "AbilityCastRange" "500 750 1000 1250" 29 | "AbilityCastPoint" "0.3" 30 | 31 | // Time 32 | //------------------------------------------------------------------------------------------------------------- 33 | "AbilityCooldown" "20 16 12 8" 34 | 35 | // Cost 36 | //------------------------------------------------------------------------------------------------------------- 37 | "AbilityManaCost" "100" 38 | 39 | // Special 40 | //------------------------------------------------------------------------------------------------------------- 41 | "AbilitySpecial" 42 | { 43 | "01" 44 | { 45 | "var_type" "FIELD_FLOAT" 46 | "duration" "2.0" 47 | } 48 | "02" 49 | { 50 | "var_type" "FIELD_INTEGER" 51 | "radius" "220" 52 | } 53 | "03" 54 | { 55 | "var_type" "FIELD_INTEGER" 56 | "speed" "857" 57 | } 58 | "04" 59 | { 60 | "var_type" "FIELD_INTEGER" 61 | "tooltip_range" "500 750 1000 1250" 62 | } 63 | // Adding cooldown and praying this gets synced 64 | "05" 65 | { 66 | "var_type" "FIELD_FLOAT" 67 | "cooldown" "20 16 12 8" 68 | "LinkedSpecialBonus" "special_bonus_unique_meepo3" 69 | } 70 | 71 | } 72 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1" 73 | } 74 | 75 | //================================================================================================================= 76 | // Meepo: Poof 77 | //================================================================================================================= 78 | "meepo_poof_lua" 79 | { 80 | // General 81 | //------------------------------------------------------------------------------------------------------------- 82 | "BaseClass" "ability_lua" 83 | "ScriptFile" "heroes/meepo/poof.lua" 84 | "AbilityTextureName" "meepo_poof" 85 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT | | DOTA_ABILITY_BEHAVIOR_UNIT_TARGET | DOTA_ABILITY_BEHAVIOR_NORMAL_WHEN_STOLEN | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING | DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES" 86 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_FRIENDLY" 87 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO" 88 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 89 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 90 | "FightRecapLevel" "1" 91 | "AbilitySound" "Hero_Meepo.Poof" 92 | 93 | // Casting 94 | //------------------------------------------------------------------------------------------------------------- 95 | "AbilityCastPoint" "1.5" 96 | "AbilityChannelTime" "0.0" 97 | 98 | // Time 99 | //------------------------------------------------------------------------------------------------------------- 100 | "AbilityCooldown" "12 10 8 6" 101 | 102 | // Cost 103 | //------------------------------------------------------------------------------------------------------------- 104 | "AbilityManaCost" "80" 105 | 106 | // Special 107 | //------------------------------------------------------------------------------------------------------------- 108 | "AbilitySpecial" 109 | { 110 | "01" 111 | { 112 | "var_type" "FIELD_INTEGER" 113 | "radius" "375" 114 | } 115 | "02" 116 | { 117 | "var_type" "FIELD_INTEGER" 118 | "poof_damage" "80 100 120 140" 119 | "LinkedSpecialBonus" "special_bonus_unique_meepo_2" 120 | } 121 | "03" 122 | { 123 | "var_type" "FIELD_INTEGER" 124 | "cooldown" "12 10 8 6" 125 | "LinkedSpecialBonus" "special_bonus_unique_meepo" 126 | } 127 | } 128 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_2" 129 | } 130 | 131 | //================================================================================================================= 132 | // Meepo: Geostrike 133 | //================================================================================================================= 134 | "meepo_geostrike_lua" 135 | { 136 | // General 137 | //------------------------------------------------------------------------------------------------------------- 138 | "BaseClass" "ability_lua" 139 | "ScriptFile" "heroes/meepo/geostrike.lua" 140 | "AbilityTextureName" "meepo_geostrike" 141 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 142 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_YES" 143 | "SpellDispellableType" "SPELL_DISPELLABLE_YES" 144 | "AbilitySound" "Hero_Meepo.Geostrike" 145 | 146 | "AbilityDamage" "10 20 30 40" 147 | 148 | "AbilityDuration" "2" 149 | 150 | // Stats 151 | //------------------------------------------------------------------------------------------------------------- 152 | "AbilityModifierSupportBonus" "5" 153 | 154 | // Special 155 | //------------------------------------------------------------------------------------------------------------- 156 | "AbilitySpecial" 157 | { 158 | "01" 159 | { 160 | "var_type" "FIELD_INTEGER" 161 | "slow" "-5 -10 -15 -20" 162 | } 163 | "02" 164 | { 165 | "var_type" "FIELD_FLOAT" 166 | "duration_tooltip" "2.0" 167 | } 168 | } 169 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_3" 170 | } 171 | 172 | //================================================================================================================= 173 | // Meepo: Divided We Stand 174 | //================================================================================================================= 175 | "meepo_divided_we_stand_lua" 176 | { 177 | // General 178 | //------------------------------------------------------------------------------------------------------------- 179 | "BaseClass" "ability_lua" 180 | "ScriptFile" "heroes/meepo/divided_we_stand.lua" 181 | "AbilityTextureName" "meepo_divided_we_stand" // unique ID number for this ability. Do not change this once established or it will invalidate collected stats. 182 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 183 | "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" 184 | "DisplayAdditionalHeroes" "1" 185 | "LevelsBetweenUpgrades" "7" 186 | "RequiredLevel" "3" 187 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_4" 188 | 189 | "HasScepterUpgrade" "1" 190 | 191 | // Special 192 | //------------------------------------------------------------------------------------------------------------- 193 | "AbilitySpecial" 194 | { 195 | "01" 196 | { 197 | "var_type" "FIELD_INTEGER" 198 | "tooltip_clones" "1 2 3" 199 | } 200 | "02" 201 | { 202 | "var_type" "FIELD_INTEGER" 203 | "tooltip_share_percentage" "20" 204 | } 205 | "03" 206 | { 207 | "var_type" "FIELD_INTEGER" 208 | "tooltip_share_percentage_scepter" "100" 209 | } 210 | "04" 211 | { 212 | "var_type" "FIELD_FLOAT" 213 | "respawn" "0.0" 214 | } 215 | "05" 216 | { 217 | "var_type" "FIELD_INTEGER" 218 | "tooltip_respawn" "20" 219 | } 220 | } 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /game/scripts/npc/heroes/skywrath_mage.kv: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary spells for Skywrath Mage /// 3 | /// /// 4 | /// Last Update: 7.07c /// 5 | /// /// 6 | 7 | "DOTAAbilities" 8 | { 9 | //================================================================================================================= 10 | // Skywrath Mage: Arcane Bolt 11 | //================================================================================================================= 12 | "lua_skywrath_mage_arcane_bolt" 13 | { 14 | // General 15 | //------------------------------------------------------------------------------------------------------------- 16 | "BaseClass" "ability_lua" 17 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" 18 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY" 19 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" 20 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 21 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 22 | "FightRecapLevel" "1" 23 | "AbilitySound" "Hero_SkywrathMage.ArcaneBolt.Cast" 24 | "ScriptFile" "heroes/skywrath_mage/skywrath_mage_arcane_bolt" 25 | 26 | // Casting 27 | //------------------------------------------------------------------------------------------------------------- 28 | "AbilityCastRange" "875" 29 | "AbilityCastPoint" "0.1 0.1 0.1 0.1" 30 | 31 | // Time 32 | //------------------------------------------------------------------------------------------------------------- 33 | "AbilityCooldown" "5.0 4.0 3.0 2.0" 34 | 35 | // Cost 36 | //------------------------------------------------------------------------------------------------------------- 37 | "AbilityManaCost" "70 70 70 70" 38 | 39 | // Special 40 | //------------------------------------------------------------------------------------------------------------- 41 | "AbilitySpecial" 42 | { 43 | "01" 44 | { 45 | "var_type" "FIELD_INTEGER" 46 | "bolt_speed" "500" 47 | } 48 | "02" 49 | { 50 | "var_type" "FIELD_INTEGER" 51 | "bolt_vision" "325" 52 | } 53 | "03" 54 | { 55 | "var_type" "FIELD_FLOAT" 56 | "bolt_damage" "60 80 100 120" 57 | } 58 | "04" 59 | { 60 | "var_type" "FIELD_FLOAT" 61 | "int_multiplier" "1.6" 62 | "CalculateSpellDamageTooltip" "1" 63 | } 64 | "05" 65 | { 66 | "var_type" "FIELD_FLOAT" 67 | "vision_duration" "3.34" 68 | } 69 | "06" 70 | { 71 | "var_type" "FIELD_INTEGER" 72 | "scepter_radius" "700" 73 | } 74 | } 75 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1" 76 | } 77 | 78 | //================================================================================================================= 79 | // Skywrath Mage: Concussive Shot 80 | //================================================================================================================= 81 | "lua_skywrath_mage_concussive_shot" 82 | { 83 | // General 84 | //------------------------------------------------------------------------------------------------------------- 85 | "BaseClass" "ability_lua" 86 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_NO_TARGET" 87 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 88 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 89 | "SpellDispellableType" "SPELL_DISPELLABLE_YES" 90 | "FightRecapLevel" "1" 91 | "AbilitySound" "Hero_SkywrathMage.ConcussiveShot.Cast" 92 | "ScriptFile" "heroes/skywrath_mage/skywrath_mage_concussive_shot" 93 | 94 | // Casting 95 | //------------------------------------------------------------------------------------------------------------- 96 | "AbilityCastPoint" "0.0 0.0 0.0 0.0" 97 | "AbilityCastRange" "1600" 98 | 99 | // Time 100 | //------------------------------------------------------------------------------------------------------------- 101 | "AbilityCooldown" "18.0 16.0 14.0 12.0" 102 | 103 | 104 | // Cost 105 | //------------------------------------------------------------------------------------------------------------- 106 | "AbilityManaCost" "95" 107 | 108 | 109 | // Special 110 | //------------------------------------------------------------------------------------------------------------- 111 | "AbilitySpecial" 112 | { 113 | "01" 114 | { 115 | "var_type" "FIELD_INTEGER" 116 | "launch_radius" "1600" 117 | } 118 | "02" 119 | { 120 | "var_type" "FIELD_INTEGER" 121 | "slow_radius" "250" 122 | } 123 | "03" 124 | { 125 | "var_type" "FIELD_INTEGER" 126 | "speed" "800 800 800 800" 127 | } 128 | "04" 129 | { 130 | "var_type" "FIELD_INTEGER" 131 | "damage" "70 140 210 280" 132 | } 133 | "05" 134 | { 135 | "var_type" "FIELD_FLOAT" 136 | "slow_duration" "4.0" 137 | } 138 | "06" 139 | { 140 | "var_type" "FIELD_INTEGER" 141 | "movement_speed_pct" "30 35 40 45" 142 | } 143 | "07" 144 | { 145 | "var_type" "FIELD_INTEGER" 146 | "shot_vision" "300" 147 | } 148 | "08" 149 | { 150 | "var_type" "FIELD_FLOAT" 151 | "vision_duration" "3.34" 152 | } 153 | "09" 154 | { 155 | "var_type" "FIELD_INTEGER" 156 | "scepter_radius" "700" 157 | } 158 | } 159 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_2" 160 | } 161 | 162 | //================================================================================================================= 163 | // Skywrath Mage: Ancient Seal 164 | //================================================================================================================= 165 | "lua_skywrath_mage_ancient_seal" 166 | { 167 | // General 168 | //------------------------------------------------------------------------------------------------------------- 169 | "BaseClass" "ability_lua" 170 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" 171 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY" 172 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" 173 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 174 | "SpellDispellableType" "SPELL_DISPELLABLE_YES" 175 | "AbilitySound" "Hero_SkywrathMage.AncientSeal.Target" 176 | "ScriptFile" "heroes/skywrath_mage/skywrath_mage_arcane_seal" 177 | 178 | // Casting 179 | //------------------------------------------------------------------------------------------------------------- 180 | "AbilityCastRange" "700" 181 | "AbilityCastPoint" "0.1 0.1 0.1 0.1" 182 | 183 | // Time 184 | //------------------------------------------------------------------------------------------------------------- 185 | "AbilityCooldown" "14" 186 | 187 | // Cost 188 | //------------------------------------------------------------------------------------------------------------- 189 | "AbilityManaCost" "80 90 100 110" 190 | 191 | // Special 192 | //------------------------------------------------------------------------------------------------------------- 193 | "AbilitySpecial" 194 | { 195 | "01" 196 | { 197 | "var_type" "FIELD_INTEGER" 198 | "resist_debuff" "-30 -35 -40 -45" 199 | "LinkedSpecialBonus" "special_bonus_unique_skywrath_3" 200 | } 201 | "02" 202 | { 203 | "var_type" "FIELD_FLOAT" 204 | "seal_duration" "3.0 4.0 5.0 6.0" 205 | } 206 | "03" 207 | { 208 | "var_type" "FIELD_INTEGER" 209 | "scepter_radius" "700" 210 | } 211 | } 212 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_3" 213 | } 214 | 215 | //================================================================================================================= 216 | // Skywrath Mage: Mystic Flare 217 | //================================================================================================================= 218 | "lua_skywrath_mage_mystic_flare" 219 | { 220 | // General 221 | //------------------------------------------------------------------------------------------------------------- 222 | "BaseClass" "ability_lua" 223 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_AOE | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING" 224 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 225 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO" 226 | "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" 227 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 228 | "FightRecapLevel" "2" 229 | "ScriptFile" "heroes/skywrath_mage/skywrath_mage_mystic_flare" 230 | 231 | "HasScepterUpgrade" "1" 232 | 233 | // Casting 234 | //------------------------------------------------------------------------------------------------------------- 235 | "AbilityCastRange" "1200" 236 | "AbilityCastPoint" "0.1 0.1 0.1 0.1" 237 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_4" 238 | 239 | // Time 240 | //------------------------------------------------------------------------------------------------------------- 241 | "AbilityCooldown" "60.0 40.0 20.0" 242 | 243 | // Cost 244 | //------------------------------------------------------------------------------------------------------------- 245 | "AbilityManaCost" "300 550 800" 246 | 247 | // Special 248 | //------------------------------------------------------------------------------------------------------------- 249 | "AbilitySpecial" 250 | { 251 | "01" 252 | { 253 | "var_type" "FIELD_INTEGER" 254 | "radius" "170" 255 | } 256 | "02" 257 | { 258 | "var_type" "FIELD_FLOAT" 259 | "duration" "2.4" 260 | } 261 | "03" 262 | { 263 | "var_type" "FIELD_INTEGER" 264 | "damage" "600 1000 1400" 265 | "LinkedSpecialBonus" "special_bonus_unique_skywrath_5" 266 | } 267 | "04" 268 | { 269 | "var_type" "FIELD_FLOAT" 270 | "damage_interval" "0.1" 271 | } 272 | "05" 273 | { 274 | "var_type" "FIELD_INTEGER" 275 | "scepter_radius" "700" 276 | } 277 | } 278 | } 279 | } -------------------------------------------------------------------------------- /game/scripts/npc/heroes/sniper.kv: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary spells for sniper /// 3 | /// /// 4 | /// Numbers correct as at 7.07c /// 5 | /// /// 6 | 7 | "DOTAAbilities" 8 | { 9 | "sniper_shrapnel_lua" 10 | { 11 | // General 12 | //------------------------------------------------------------------------------------------------------------- 13 | "BaseClass" "ability_lua" 14 | "ScriptFile" "heroes/sniper/shrapnel.lua" 15 | "AbilityTextureName" "sniper_shrapnel" 16 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_AOE | DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING" 17 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 18 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 19 | "SpellDispellableType" "SPELL_DISPELLABLE_NO" 20 | "FightRecapLevel" "1" 21 | "AbilitySound" "Hero_Sniper.ShrapnelShatter" 22 | 23 | // Casting 24 | //------------------------------------------------------------------------------------------------------------- 25 | "AbilityCastRange" "1800" 26 | "AbilityCastPoint" "0.3 0.3 0.3 0.3" 27 | 28 | // Time 29 | //------------------------------------------------------------------------------------------------------------- 30 | "AbilityCooldown" "0" 31 | 32 | 33 | // Cost 34 | //------------------------------------------------------------------------------------------------------------- 35 | "AbilityManaCost" "50" 36 | 37 | // Stats 38 | //------------------------------------------------------------------------------------------------------------- 39 | "AbilityModifierSupportValue" "0.25" // primarily about damage 40 | 41 | // Special 42 | //------------------------------------------------------------------------------------------------------------- 43 | "AbilitySpecial" 44 | { 45 | "01" 46 | { 47 | "var_type" "FIELD_INTEGER" 48 | "slow_movement_speed" "-15 -20 -25 -30" 49 | } 50 | "02" 51 | { 52 | "var_type" "FIELD_INTEGER" 53 | "radius" "450" 54 | } 55 | "03" 56 | { 57 | "var_type" "FIELD_INTEGER" 58 | "shrapnel_damage" "15 35 55 75" 59 | "LinkedSpecialBonus" "special_bonus_unique_sniper_1" 60 | } 61 | "04" 62 | { 63 | "var_type" "FIELD_FLOAT" 64 | "duration" "10.0" 65 | } 66 | "05" 67 | { 68 | "var_type" "FIELD_FLOAT" 69 | "damage_delay" "1.2" 70 | } 71 | "06" 72 | { 73 | "var_type" "FIELD_FLOAT" 74 | "slow_duration" "2.0 2.0 2.0 2.0" 75 | } 76 | "07" 77 | { 78 | "var_type" "FIELD_INTEGER" 79 | "max_charges" "3" 80 | "LinkedSpecialBonus" "special_bonus_unique_sniper_2" 81 | } 82 | "08" 83 | { 84 | "var_type" "FIELD_INTEGER" 85 | "charge_restore_time" "55" 86 | } 87 | } 88 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1" 89 | } 90 | 91 | //================================================================================================================= 92 | // Sniper: Headshot 93 | //================================================================================================================= 94 | "sniper_headshot_lua" 95 | { 96 | // General 97 | //------------------------------------------------------------------------------------------------------------- 98 | "BaseClass" "ability_lua" 99 | "ScriptFile" "heroes/sniper/headshot.lua" 100 | "AbilityTextureName" "sniper_headshot" 101 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 102 | "AbilityUnitDamageType" "DAMAGE_TYPE_PHYSICAL" 103 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 104 | 105 | // Damage. 106 | //------------------------------------------------------------------------------------------------------------- 107 | "AbilityDamage" "15 40 65 90" 108 | 109 | // Special 110 | //------------------------------------------------------------------------------------------------------------- 111 | "AbilitySpecial" 112 | { 113 | "01" 114 | { 115 | "var_type" "FIELD_FLOAT" 116 | "slow_duration" "0.5" 117 | } 118 | "02" 119 | { 120 | "var_type" "FIELD_INTEGER" 121 | "proc_chance" "40" 122 | } 123 | "03" 124 | { 125 | "var_type" "FIELD_INTEGER" 126 | "slow" "-100" 127 | } 128 | } 129 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_2" 130 | } 131 | 132 | //================================================================================================================= 133 | // Sniper: Take Aim 134 | //================================================================================================================= 135 | "sniper_take_aim_lua" 136 | { 137 | // General 138 | //------------------------------------------------------------------------------------------------------------- 139 | "BaseClass" "ability_lua" 140 | "ScriptFile" "heroes/sniper/take_aim.lua" 141 | "AbilityTextureName" "sniper_take_aim" 142 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 143 | 144 | // Special 145 | //------------------------------------------------------------------------------------------------------------- 146 | "AbilitySpecial" 147 | { 148 | "01" 149 | { 150 | "var_type" "FIELD_INTEGER" 151 | "bonus_attack_range" "100 200 300 400" 152 | } 153 | } 154 | } 155 | 156 | //================================================================================================================= 157 | // Sniper: Assassinate 158 | //================================================================================================================= 159 | "sniper_assassinate_lua" 160 | { 161 | // General 162 | //------------------------------------------------------------------------------------------------------------- 163 | "BaseClass" "ability_lua" 164 | "ScriptFile" "heroes/sniper/assassinate.lua" 165 | "AbilityTextureName" "sniper_assassinate" 166 | "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" 167 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY" 168 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" 169 | "AbilityUnitTargetFlags" "DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES | DOTA_UNIT_TARGET_FLAG_INVULNERABLE" 170 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_YES" 171 | "SpellDispellableType" "SPELL_DISPELLABLE_NO" 172 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 173 | "FightRecapLevel" "2" 174 | "HasScepterUpgrade" "1" 175 | "AbilitySound" "Ability.Assassinate" 176 | 177 | // Casting 178 | //------------------------------------------------------------------------------------------------------------- 179 | "AbilityCastRange" "2000 2500 3000" 180 | "AbilityCastRangeBuffer" "600" 181 | "AbilityCastPoint" "2.0 2.0 2.0" 182 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_4" 183 | 184 | // Time 185 | //------------------------------------------------------------------------------------------------------------- 186 | "AbilityCooldown" "20.0 15.0 10.0" 187 | 188 | // Damage. 189 | //------------------------------------------------------------------------------------------------------------- 190 | "AbilityDamage" "320 485 650" 191 | 192 | // Cost 193 | //------------------------------------------------------------------------------------------------------------- 194 | "AbilityManaCost" "175 275 375" 195 | 196 | // Stats 197 | //------------------------------------------------------------------------------------------------------------- 198 | "AbilityModifierSupportValue" "0.0" // Modifier just does damage 199 | 200 | // Special 201 | //------------------------------------------------------------------------------------------------------------- 202 | "AbilitySpecial" 203 | { 204 | "01" 205 | { 206 | "var_type" "FIELD_INTEGER" 207 | "projectile_speed" "2500 2500 2500" 208 | } 209 | "02" 210 | { 211 | "var_type" "FIELD_INTEGER" 212 | "tooltip_range" "2000 2500 3000" 213 | } 214 | "03" 215 | { 216 | "var_type" "FIELD_INTEGER" 217 | "total_cast_time_tooltip" "2" 218 | } 219 | "04" 220 | { 221 | "var_type" "FIELD_INTEGER" 222 | "scepter_radius" "400" 223 | } 224 | "05" 225 | { 226 | "var_type" "FIELD_INTEGER" 227 | "scepter_crit_bonus" "280" 228 | } 229 | } 230 | } 231 | "special_bonus_unique_sniper_1" 232 | { 233 | // General 234 | //------------------------------------------------------------------------------------------------------------- 235 | "BaseClass" "special_bonus_unique_centaur_2" // Doesn't matter! 236 | "AbilityType" "DOTA_ABILITY_TYPE_ATTRIBUTES" 237 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 238 | 239 | // Special 240 | //------------------------------------------------------------------------------------------------------------- 241 | "AbilitySpecial" 242 | { 243 | "01" 244 | { 245 | "var_type" "FIELD_INTEGER" 246 | "value" "25" 247 | } 248 | } 249 | } 250 | 251 | "special_bonus_unique_sniper_2" // 252 | { 253 | // General 254 | //------------------------------------------------------------------------------------------------------------- 255 | "BaseClass" "special_bonus_unique_centaur_2" // Doesn't matter! 256 | "AbilityType" "DOTA_ABILITY_TYPE_ATTRIBUTES" 257 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 258 | 259 | // Special 260 | //------------------------------------------------------------------------------------------------------------- 261 | "AbilitySpecial" 262 | { 263 | "01" 264 | { 265 | "var_type" "FIELD_INTEGER" 266 | "value" "6" 267 | } 268 | } 269 | } 270 | 271 | "special_bonus_unique_sniper_3" // 272 | { 273 | // General 274 | //------------------------------------------------------------------------------------------------------------- 275 | "BaseClass" "special_bonus_unique_centaur_2" // Doesn't matter! 276 | "AbilityType" "DOTA_ABILITY_TYPE_ATTRIBUTES" 277 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 278 | 279 | // Special 280 | //------------------------------------------------------------------------------------------------------------- 281 | "AbilitySpecial" 282 | { 283 | "01" 284 | { 285 | "var_type" "FIELD_INTEGER" 286 | "value" "35" 287 | } 288 | } 289 | } 290 | 291 | "special_bonus_unique_sniper_4" // 292 | { 293 | // General 294 | //------------------------------------------------------------------------------------------------------------- 295 | "BaseClass" "special_bonus_unique_centaur_2" // Doesn't matter! 296 | "AbilityType" "DOTA_ABILITY_TYPE_ATTRIBUTES" 297 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" 298 | 299 | // Special 300 | //------------------------------------------------------------------------------------------------------------- 301 | "AbilitySpecial" 302 | { 303 | "01" 304 | { 305 | "var_type" "FIELD_FLOAT" 306 | "value" "1.5" 307 | } 308 | } 309 | } 310 | 311 | } -------------------------------------------------------------------------------- /game/scripts/npc/heroes/vengefulspirit.kv: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary spells for Vengeful Spirit /// 3 | /// /// 4 | /// Numbers correct as at 7.07c /// 5 | /// /// 6 | 7 | "DOTAAbilities" 8 | { 9 | //================================================================================================================= 10 | // Ability: Vengefulspirit Magic Missle 11 | //================================================================================================================= 12 | "vengefulspirit_magic_missile_lua" 13 | { 14 | "BaseClass" "ability_lua" 15 | "ScriptFile" "heroes/vengefulspirit/magic_missle.lua" 16 | "AbilityTextureName" "vengefulspirit_magic_missile" 17 | 18 | // General 19 | //------------------------------------------------------------------------------------------------------------- 20 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" 21 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_ENEMY" 22 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" 23 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 24 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" 25 | "SpellDispellableType" "SPELL_DISPELLABLE_YES_STRONG" 26 | "FightRecapLevel" "1" 27 | "AbilitySound" "Hero_VengefulSpirit.MagicMissile" 28 | 29 | // Casting 30 | //------------------------------------------------------------------------------------------------------------- 31 | "AbilityCastRange" "500" 32 | "AbilityCastPoint" "0.3 0.3 0.3 0.3" 33 | 34 | // Time 35 | //------------------------------------------------------------------------------------------------------------- 36 | "AbilityCooldown" "13 12 11 10" 37 | 38 | // Cost 39 | //------------------------------------------------------------------------------------------------------------- 40 | "AbilityManaCost" "110 120 130 140" 41 | 42 | // Special 43 | //------------------------------------------------------------------------------------------------------------- 44 | "AbilitySpecial" 45 | { 46 | "01" 47 | { 48 | "var_type" "FIELD_INTEGER" 49 | "magic_missile_speed" "1250" 50 | } 51 | "02" 52 | { 53 | "var_type" "FIELD_FLOAT" 54 | "magic_missile_stun" "1.2 1.4 1.6 1.8" 55 | } 56 | "03" 57 | { 58 | "var_type" "FIELD_INTEGER" 59 | "magic_missile_damage" "100 175 250 325" 60 | "LinkedSpecialBonus" "special_bonus_unique_vengeful_spirit_1" 61 | } 62 | } 63 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1" 64 | } 65 | 66 | //================================================================================================================= 67 | // Ability: Vengefulspirit Command Aura 68 | //================================================================================================================= 69 | "vengefulspirit_command_aura_lua" 70 | { 71 | "BaseClass" "ability_lua" 72 | "ScriptFile" "heroes/vengefulspirit/command_aura.lua" 73 | "AbilityTextureName" "vengefulspirit_command_aura" 74 | 75 | // General 76 | //------------------------------------------------------------------------------------------------------------- 77 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE | DOTA_ABILITY_BEHAVIOR_AURA" 78 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_FRIENDLY" 79 | 80 | 81 | // Casting 82 | //------------------------------------------------------------------------------------------------------------- 83 | "AbilityCastRange" "1200" 84 | 85 | // Special 86 | //------------------------------------------------------------------------------------------------------------- 87 | "AbilitySpecial" 88 | { 89 | "01" 90 | { 91 | "var_type" "FIELD_INTEGER" 92 | "bonus_damage_pct" "12 18 24 30" 93 | "LinkedSpecialBonus" "special_bonus_unique_vengeful_spirit_2" 94 | } 95 | "02" 96 | { 97 | "var_type" "FIELD_INTEGER" 98 | "aura_radius" "1200" 99 | } 100 | "03" 101 | { 102 | "var_type" "FIELD_INTEGER" 103 | "images_do_damage_percent" "0" 104 | } 105 | "04" 106 | { 107 | "var_type" "FIELD_INTEGER" 108 | "images_do_damage_percent_tooltip" "100" 109 | } 110 | "05" 111 | { 112 | "var_type" "FIELD_INTEGER" 113 | "images_take_damage_percent" "100" 114 | } 115 | "06" 116 | { 117 | "var_type" "FIELD_INTEGER" 118 | "images_take_damage_percent_tooltip" "200" 119 | } 120 | "07" 121 | { 122 | "var_type" "FIELD_FLOAT" 123 | "illusion_duration" "6" 124 | } 125 | } 126 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_3" 127 | } 128 | 129 | //================================================================================================================= 130 | // Ability: Vengefulspirit Wave of Terror 131 | //================================================================================================================= 132 | "vengefulspirit_wave_of_terror_lua" 133 | { 134 | "BaseClass" "ability_lua" 135 | "ScriptFile" "heroes/vengefulspirit/wave_of_terror.lua" 136 | "AbilityTextureName" "vengefulspirit_wave_of_terror" 137 | 138 | // General 139 | //------------------------------------------------------------------------------------------------------------- 140 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT" 141 | "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" 142 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_YES" 143 | "SpellDispellableType" "SPELL_DISPELLABLE_YES" 144 | "AbilitySound" "Hero_VengefulSpirit.WaveOfTerror" 145 | 146 | // Casting 147 | //------------------------------------------------------------------------------------------------------------- 148 | "AbilityCastRange" "1400" 149 | "AbilityCastPoint" "0.3 0.3 0.3 0.3" 150 | 151 | // Time 152 | //------------------------------------------------------------------------------------------------------------- 153 | "AbilityCooldown" "10" 154 | "AbilityDuration" "8" 155 | 156 | // Damage. 157 | //------------------------------------------------------------------------------------------------------------- 158 | "AbilityDamage" "45 70 95 120" 159 | 160 | // Cost 161 | //------------------------------------------------------------------------------------------------------------- 162 | "AbilityManaCost" "40 40 40 40" 163 | 164 | // Special 165 | //------------------------------------------------------------------------------------------------------------- 166 | "AbilitySpecial" 167 | { 168 | "01" 169 | { 170 | "var_type" "FIELD_FLOAT" 171 | "wave_speed" "2000.0" 172 | } 173 | "02" 174 | { 175 | "var_type" "FIELD_INTEGER" 176 | "wave_width" "300" 177 | } 178 | "03" 179 | { 180 | "var_type" "FIELD_INTEGER" 181 | "armor_reduction" "-3 -4 -5 -6" 182 | "LinkedSpecialBonus" "special_bonus_unique_vengeful_spirit_4" 183 | } 184 | "04" 185 | { 186 | "var_type" "FIELD_FLOAT" 187 | "tooltip_duration" "8" 188 | } 189 | "05" 190 | { 191 | "var_type" "FIELD_FLOAT" 192 | "vision_aoe" "300" 193 | } 194 | "06" 195 | { 196 | "var_type" "FIELD_FLOAT" 197 | "vision_duration" "3.0 3.0 3.0 3.0" 198 | } 199 | 200 | } 201 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_2" 202 | } 203 | 204 | //================================================================================================================= 205 | // Ability: Vengefulspirit Nether Swap 206 | //================================================================================================================= 207 | "vengefulspirit_nether_swap_lua" 208 | { 209 | "BaseClass" "ability_lua" 210 | "ScriptFile" "heroes/vengefulspirit/nether_swap.lua" 211 | "AbilityTextureName" "vengefulspirit_nether_swap" 212 | 213 | // General 214 | //------------------------------------------------------------------------------------------------------------- 215 | "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" 216 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" 217 | "AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_CUSTOM" 218 | "AbilityUnitTargetType" "DOTA_UNIT_TARGET_CUSTOM" 219 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_YES" 220 | "FightRecapLevel" "2" 221 | "AbilitySound" "Hero_VengefulSpirit.NetherSwap" 222 | 223 | "HasScepterUpgrade" "1" 224 | 225 | // Casting 226 | //------------------------------------------------------------------------------------------------------------- 227 | "AbilityCastRange" "700 950 1200" 228 | "AbilityCastPoint" "0.3 0.3 0.3" 229 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_4" 230 | 231 | // Time 232 | //------------------------------------------------------------------------------------------------------------- 233 | "AbilityCooldown" "45.0" 234 | 235 | // Cost 236 | //------------------------------------------------------------------------------------------------------------- 237 | "AbilityManaCost" "100 150 200" 238 | 239 | // Special 240 | //------------------------------------------------------------------------------------------------------------- 241 | "AbilitySpecial" 242 | { 243 | "01" 244 | { 245 | "var_type" "FIELD_INTEGER" 246 | "tooltip_range" "700 950 1200" 247 | } 248 | "02" 249 | { 250 | "var_type" "FIELD_FLOAT" 251 | "nether_swap_cooldown_scepter" "10" 252 | } 253 | "03" 254 | { 255 | "var_type" "FIELD_FLOAT" 256 | "illusion_damage_out_pct_scepter" "100" 257 | } 258 | "04" 259 | { 260 | "var_type" "FIELD_INTEGER" 261 | "illusion_damage_in_pct_scepter" "150" 262 | } 263 | } 264 | } 265 | } -------------------------------------------------------------------------------- /game/scripts/npc/npc_abilities_custom.txt: -------------------------------------------------------------------------------- 1 | #base "heroes/vengefulspirit.kv" 2 | #base "heroes/antimage.kv" 3 | #base "heroes/tusk.kv" 4 | #base "heroes/skywrath_mage.kv" 5 | #base "heroes/sniper.kv" 6 | #base "heroes/meepo.kv" 7 | 8 | "DOTAAbilities" 9 | { 10 | "Version" "1" 11 | "base_ability_charges" 12 | { 13 | "BaseClass" "ability_lua" 14 | "ScriptFile" "heroes/util/base_ability_charges.lua" 15 | } 16 | } -------------------------------------------------------------------------------- /game/scripts/npc/npc_heroes_custom.txt: -------------------------------------------------------------------------------- 1 | /// /// 2 | /// ModDota AbilityLuaSpellLibrary hero override table /// 3 | /// /// 4 | 5 | "DOTAHeroes" 6 | { 7 | // Anti Mage // Last update: 7.07c 8 | "npc_dota_hero_antimage_lua" 9 | { 10 | "override_hero" "npc_dota_hero_antimage" 11 | 12 | "Ability1" "lua_antimage_mana_break" 13 | "Ability2" "lua_antimage_blink" 14 | "Ability3" "lua_antimage_spell_shield" 15 | "Ability4" "generic_hidden" 16 | "Ability5" "generic_hidden" 17 | "Ability6" "lua_antimage_mana_void" 18 | "Ability10" "special_bonus_hp_175" 19 | "Ability11" "special_bonus_attack_speed_20" 20 | "Ability12" "special_bonus_unique_antimage" 21 | "Ability13" "special_bonus_agility_15" 22 | "Ability14" "special_bonus_unique_antimage_5" 23 | "Ability15" "special_bonus_unique_antimage_3" 24 | "Ability16" "special_bonus_unique_antimage_4" 25 | "Ability17" "special_bonus_unique_antimage_2" 26 | } 27 | 28 | // Tusk values last updated:7.07c 29 | "npc_dota_hero_tusk_lua" 30 | { 31 | "override_hero" "npc_dota_hero_tusk" 32 | 33 | "Ability1" "tusk_ice_shards_lua" 34 | "Ability2" "tusk_snowball_lua" 35 | "Ability3" "tusk_frozen_sigil_lua" 36 | "Ability4" "tusk_walrus_kick_lua" 37 | "Ability5" "generic_hidden" 38 | "Ability6" "tusk_walrus_punch_lua" 39 | "Ability7" "tusk_snowball_release_lua" 40 | "Ability10" "special_bonus_exp_boost_40" 41 | "Ability11" "special_bonus_gold_income_15" 42 | "Ability12" "special_bonus_hp_350" 43 | "Ability13" "special_bonus_unique_tusk_2_lua" 44 | "Ability14" "special_bonus_unique_tusk_3_lua" 45 | "Ability15" "special_bonus_unique_tusk_lua" 46 | "Ability16" "special_bonus_unique_tusk_4_lua" 47 | "Ability17" "special_bonus_unique_tusk_5_lua" 48 | } 49 | 50 | // Vengeful Spirit values last updated:7.07c 51 | 52 | "npc_dota_hero_vengefulspirit_lua" 53 | { 54 | "override_hero" "npc_dota_hero_vengefulspirit" 55 | 56 | "Ability1" "vengefulspirit_magic_missile_lua" 57 | "Ability2" "vengefulspirit_wave_of_terror_lua" 58 | "Ability3" "vengefulspirit_command_aura_lua" 59 | "Ability4" "generic_hidden" 60 | "Ability5" "generic_hidden" 61 | "Ability6" "vengefulspirit_nether_swap_lua" 62 | "Ability10" "special_bonus_magic_resistance_15" 63 | "Ability11" "special_bonus_unique_vengeful_spirit_4" 64 | "Ability12" "special_bonus_agility_15" 65 | "Ability13" "special_bonus_unique_vengeful_spirit_6" 66 | "Ability14" "special_bonus_unique_vengeful_spirit_5" 67 | "Ability15" "special_bonus_unique_vengeful_spirit_2" 68 | "Ability16" "special_bonus_unique_vengeful_spirit_1" 69 | "Ability17" "special_bonus_unique_vengeful_spirit_3" 70 | } 71 | 72 | // Skywrath Mage values last updated:7.07c 73 | 74 | "npc_dota_hero_skywrath_mage_lua" 75 | { 76 | "override_hero" "npc_dota_hero_skywrath_mage" 77 | 78 | "Ability1" "lua_skywrath_mage_arcane_bolt" 79 | "Ability2" "lua_skywrath_mage_concussive_shot" 80 | "Ability3" "lua_skywrath_mage_ancient_seal" 81 | "Ability4" "generic_hidden" 82 | "Ability5" "generic_hidden" 83 | "Ability6" "lua_skywrath_mage_mystic_flare" 84 | "Ability10" "special_bonus_movement_speed_25" 85 | "Ability11" "special_bonus_intelligence_8" 86 | "Ability12" "special_bonus_unique_skywrath" 87 | "Ability13" "special_bonus_unique_skywrath_2" 88 | "Ability14" "special_bonus_unique_skywrath_4" 89 | "Ability15" "special_bonus_unique_skywrath_3" 90 | "Ability16" "special_bonus_gold_income_50" 91 | "Ability17" "special_bonus_unique_skywrath_5" 92 | } 93 | // Sniper values last updated:7.07c 94 | 95 | "npc_dota_hero_sniper_lua" 96 | { 97 | "override_hero" "npc_dota_hero_sniper" 98 | 99 | "Ability1" "sniper_shrapnel_lua" 100 | "Ability2" "sniper_take_aim_lua" 101 | "Ability3" "sniper_headshot_lua" 102 | "Ability4" "generic_hidden" 103 | "Ability5" "generic_hidden" 104 | "Ability6" "sniper_assassinate_lua" 105 | "Ability10" "special_bonus_cooldown_reduction_15" 106 | "Ability11" "special_bonus_attack_damage_20" 107 | "Ability12" "special_bonus_attack_speed_40" 108 | "Ability13" "special_bonus_unique_sniper_1" 109 | "Ability14" "special_bonus_unique_sniper_3" 110 | "Ability15" "special_bonus_unique_sniper_4" 111 | "Ability16" "special_bonus_attack_range_125" 112 | "Ability17" "special_bonus_unique_sniper_2" 113 | } 114 | 115 | "npc_dota_hero_meepo" 116 | { 117 | "Ability1" "meepo_earthbind_lua" 118 | "Ability2" "meepo_poof_lua" 119 | "Ability3" "meepo_geostrike_lua" 120 | "Ability4" "generic_hidden" 121 | "Ability5" "generic_hidden" 122 | "Ability6" "meepo_divided_we_stand_lua" 123 | "Ability10" "special_bonus_strength_7" 124 | "Ability11" "special_bonus_attack_damage_20" 125 | "Ability12" "special_bonus_lifesteal_10" 126 | "Ability13" "special_bonus_unique_meepo_2" 127 | "Ability14" "special_bonus_unique_meepo_3" 128 | "Ability15" "special_bonus_evasion_15" 129 | "Ability16" "special_bonus_hp_600" 130 | "Ability17" "special_bonus_unique_meepo" 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/antimage/antimage_blink.lua: -------------------------------------------------------------------------------- 1 | -- Author: Shush 2 | -- Date: 25/11/2017 3 | 4 | ------------------------------- 5 | -- Anti Mage's Blink -- 6 | ------------------------------- 7 | 8 | lua_antimage_blink = class({}) 9 | 10 | function lua_antimage_blink:GetAbilityTextureName() 11 | return "antimage_blink" 12 | end 13 | 14 | function lua_antimage_blink:OnSpellStart() 15 | -- Ability properties 16 | local caster = self:GetCaster() 17 | local ability = self 18 | local target_point = ability:GetCursorPosition() 19 | local blink_out_sound = "Hero_Antimage.Blink_out" 20 | local blink_in_sound = "Hero_Antimage.Blink_in" 21 | local particle_blink_start = "particles/units/heroes/hero_antimage/antimage_blink_start.vpcf" 22 | local particle_blink_end = "particles/units/heroes/hero_antimage/antimage_blink_end.vpcf" 23 | 24 | -- Ability specials 25 | local blink_range = ability:GetSpecialValueFor("blink_range") 26 | 27 | -- Get blink distance 28 | local blink_distance = (target_point - caster:GetAbsOrigin()):Length2D() 29 | local direction = (target_point - caster:GetAbsOrigin()):Normalized() 30 | 31 | -- Maximum blink distance 32 | if blink_distance > blink_range then 33 | blink_distance = blink_range 34 | end 35 | 36 | -- Blink particles on starting point 37 | local blink_pfx = ParticleManager:CreateParticle(particle_blink_start, PATTACH_ABSORIGIN, caster) 38 | ParticleManager:ReleaseParticleIndex(blink_pfx) 39 | 40 | -- Blink sound on starting point 41 | EmitSoundOnLocationWithCaster(caster:GetAbsOrigin(), blink_out_sound, caster) 42 | 43 | Timers:CreateTimer(FrameTime(), function() 44 | 45 | -- Calculate location to move to 46 | local blink_point = caster:GetAbsOrigin() + direction * blink_distance 47 | 48 | -- Disjoint projectiles 49 | ProjectileManager:ProjectileDodge(caster) 50 | 51 | -- Move hero 52 | FindClearSpaceForUnit(caster, blink_point, true) 53 | 54 | -- Create Particle on end-point 55 | local blink_end_pfx = ParticleManager:CreateParticle(particle_blink_end, PATTACH_ABSORIGIN, caster) 56 | ParticleManager:ReleaseParticleIndex(blink_end_pfx) 57 | 58 | -- Blink sound on end point 59 | EmitSoundOnLocationWithCaster(caster:GetAbsOrigin(), blink_out_sound, caster) 60 | end) 61 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/antimage/antimage_mana_break.lua: -------------------------------------------------------------------------------- 1 | -- Author: Shush 2 | -- Date: 25/11/2017 3 | 4 | ------------------------------- 5 | -- Anti Mage's Mana Break -- 6 | ------------------------------- 7 | lua_antimage_mana_break = class({}) 8 | LinkLuaModifier("modifier_lua_antimage_mana_break", "antimage/antimage_mana_break", LUA_MODIFIER_MOTION_NONE) 9 | 10 | function lua_antimage_mana_break:GetIntrinsicModifierName() 11 | return "modifier_lua_antimage_mana_break" 12 | end 13 | 14 | function lua_antimage_mana_break:GetAbilityTextureName() 15 | return "antimage_mana_break" 16 | end 17 | 18 | -- Mana break's.. well, mana burn modifier 19 | modifier_lua_antimage_mana_break = class({}) 20 | 21 | function modifier_lua_antimage_mana_break:IsHidden() return true end 22 | function modifier_lua_antimage_mana_break:IsPurgable() return false end 23 | function modifier_lua_antimage_mana_break:IsDebuff() return false end 24 | function modifier_lua_antimage_mana_break:RemoveOnDeath() return false end 25 | 26 | function modifier_lua_antimage_mana_break:OnCreated() 27 | -- Ability properties 28 | self.caster = self:GetCaster() 29 | self.ability = self:GetAbility() 30 | self.hit_sound = "Hero_Antimage.ManaBreak" 31 | self.particle_manaburn = "particles/generic_gameplay/generic_manaburn.vpcf" 32 | 33 | -- Ability specials 34 | self.damage_per_burn = self.ability:GetSpecialValueFor("damage_per_burn") 35 | self.mana_per_hit = self.ability:GetSpecialValueFor("mana_per_hit") 36 | end 37 | 38 | function modifier_lua_antimage_mana_break:OnRefresh() 39 | -- Refresh specials 40 | self.damage_per_burn = self.ability:GetSpecialValueFor("damage_per_burn") 41 | self.mana_per_hit = self.ability:GetSpecialValueFor("mana_per_hit") 42 | end 43 | 44 | function modifier_lua_antimage_mana_break:DeclareFunctions() 45 | local decFuncs = {MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_PHYSICAL} 46 | 47 | return decFuncs 48 | end 49 | 50 | function modifier_lua_antimage_mana_break:GetModifierProcAttack_BonusDamage_Physical(keys) 51 | local target = keys.target 52 | local attacker = keys.attacker 53 | 54 | -- If the attacker wasn't the caster, do nothing 55 | if attacker ~= self.caster then 56 | return nil 57 | end 58 | 59 | -- If the attacker has Break, do nothing 60 | if attacker:PassivesDisabled() then 61 | return nil 62 | end 63 | 64 | -- If the target doesn't have a mana pool, do nothing 65 | if target:GetMaxMana() == 0 then 66 | return nil 67 | end 68 | 69 | -- If the target is a building or a ward, do nothing 70 | if target:IsBuilding() or target:IsOther() then 71 | return nil 72 | end 73 | 74 | -- If we got here, this means Anti Mage has landed a successful attack that mana burns! 75 | -- Play sound 76 | EmitSoundOn(self.hit_sound, target) 77 | 78 | -- Play particle effects 79 | local manaburn_pfx = ParticleManager:CreateParticle(self.particle_manaburn, PATTACH_ABSORIGIN_FOLLOW, target) 80 | ParticleManager:SetParticleControl(manaburn_pfx, 0, target:GetAbsOrigin() ) 81 | ParticleManager:ReleaseParticleIndex(manaburn_pfx) 82 | 83 | -- Calculate mana before the burn 84 | local target_mana = target:GetMana() 85 | local mana_burn = self.mana_per_hit 86 | 87 | -- If the target has less mana than the mana that will be burned, set the mana to be burned accordingly 88 | -- This is done to apply correct damage based on actual mana burned 89 | if self.mana_per_hit > target_mana then 90 | mana_burn = target_mana 91 | end 92 | 93 | -- Calculate burn damage 94 | local damage = mana_burn * self.damage_per_burn 95 | 96 | -- Reduce target mana 97 | target:ReduceMana(mana_burn) 98 | 99 | return damage 100 | end 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/antimage/antimage_mana_void.lua: -------------------------------------------------------------------------------- 1 | -- Author: Shush 2 | -- Date: 29/11/2017 3 | 4 | ------------------------------- 5 | -- Anti Mage's Mana Void -- 6 | ------------------------------- 7 | lua_antimage_mana_void = class({}) 8 | LinkLuaModifier("modifier_lua_mana_void_stunned", "antimage/antimage_mana_void", LUA_MODIFIER_MOTION_NONE) 9 | 10 | function lua_antimage_mana_void:GetAbilityTextureName() 11 | return "antimage_mana_void" 12 | end 13 | 14 | function lua_antimage_mana_void:OnAbilityPhaseStart() 15 | self:GetCaster():EmitSound("Hero_Antimage.ManaVoidCast") 16 | return true 17 | end 18 | 19 | function lua_antimage_mana_void:GetAOERadius() 20 | return self:GetSpecialValueFor("mana_void_aoe_radius") 21 | end 22 | 23 | function lua_antimage_mana_void:IsHiddenWhenStolen() 24 | return false 25 | end 26 | 27 | function lua_antimage_mana_void:OnSpellStart() 28 | -- Ability properties 29 | local caster = self:GetCaster() 30 | local ability = self 31 | local target = self:GetCursorTarget() 32 | local modifier_ministun = "modifier_lua_mana_void_stunned" 33 | 34 | -- Ability specials 35 | local mana_void_damage_per_mana = ability:GetSpecialValueFor("mana_void_damage_per_mana") 36 | local mana_void_ministun = ability:GetSpecialValueFor("mana_void_ministun") 37 | local mana_void_aoe_radius = ability:GetSpecialValueFor("mana_void_aoe_radius") 38 | 39 | -- If the target possesses a ready Linken's Sphere, do nothing 40 | if target:GetTeam() ~= caster:GetTeam() then 41 | if target:TriggerSpellAbsorb(ability) then 42 | return nil 43 | end 44 | end 45 | 46 | -- Calculate missing mana and damage based on it 47 | local damage = 0 48 | 49 | -- Damage is not calculated on targets that have no mana pool 50 | if target:GetMaxMana() > 0 then 51 | local missing_mana = target:GetMaxMana() - target:GetMana() 52 | damage = missing_mana * mana_void_damage_per_mana 53 | end 54 | 55 | -- Ministun the main target 56 | target:AddNewModifier(caster, ability, modifier_ministun, {duration = mana_void_ministun}) 57 | 58 | -- Find all nearby enemies 59 | local enemies = FindUnitsInRadius(caster:GetTeamNumber(), 60 | target:GetAbsOrigin(), 61 | nil, 62 | mana_void_aoe_radius, 63 | DOTA_UNIT_TARGET_TEAM_ENEMY, 64 | DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC, 65 | DOTA_UNIT_TARGET_FLAG_NONE, 66 | FIND_ANY_ORDER, 67 | false) 68 | 69 | -- Damage all enemies in the area for the total damage tally 70 | for _,enemy in pairs(enemies) do 71 | if not enemy:IsMagicImmune() then 72 | 73 | -- Deal damage 74 | local damageTable = {victim = enemy, 75 | damage = damage, 76 | damage_type = DAMAGE_TYPE_MAGICAL, 77 | attacker = caster, 78 | ability = ability 79 | } 80 | 81 | ApplyDamage(damageTable) 82 | SendOverheadEventMessage(nil, OVERHEAD_ALERT_BONUS_SPELL_DAMAGE, enemy, damage, nil) 83 | end 84 | end 85 | 86 | -- Mana Void effects 87 | local void_pfx = ParticleManager:CreateParticle("particles/units/heroes/hero_antimage/antimage_manavoid.vpcf", PATTACH_POINT_FOLLOW, target) 88 | ParticleManager:SetParticleControlEnt(void_pfx, 0, target, PATTACH_POINT_FOLLOW, "attach_hitloc", target:GetOrigin(), true) 89 | ParticleManager:SetParticleControl(void_pfx, 1, Vector(mana_void_aoe_radius,0,0)) 90 | ParticleManager:ReleaseParticleIndex(void_pfx) 91 | target:EmitSound("Hero_Antimage.ManaVoid") 92 | end 93 | 94 | 95 | -- Stun modifier 96 | modifier_lua_mana_void_stunned = class({}) 97 | 98 | function modifier_lua_mana_void_stunned:CheckState() 99 | local state = {[MODIFIER_STATE_STUNNED] = true} 100 | return state 101 | end 102 | 103 | function modifier_lua_mana_void_stunned:IsPurgable() return false end 104 | function modifier_lua_mana_void_stunned:IsPurgeException() return true end 105 | function modifier_lua_mana_void_stunned:IsStunDebuff() return true end 106 | function modifier_lua_mana_void_stunned:IsHidden() return false end 107 | function modifier_lua_mana_void_stunned:GetEffectName() return "particles/generic_gameplay/generic_stunned.vpcf" end 108 | function modifier_lua_mana_void_stunned:GetEffectAttachType() return PATTACH_OVERHEAD_FOLLOW end 109 | function modifier_lua_mana_void_stunned:DeclareFunctions() 110 | local decFuncs = {MODIFIER_PROPERTY_OVERRIDE_ANIMATION} 111 | 112 | return decFuncs 113 | end 114 | 115 | function modifier_lua_mana_void_stunned:GetOverrideAnimation() 116 | return ACT_DOTA_DISABLED 117 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/antimage/antimage_spell_shield.lua: -------------------------------------------------------------------------------- 1 | -- Author: Shush 2 | -- Date: 25/11/2017 3 | 4 | ------------------------------- 5 | -- Anti Mage's Spell Shield -- 6 | ------------------------------- 7 | lua_antimage_spell_shield = class({}) 8 | LinkLuaModifier("modifier_lua_antimage_spell_shield", "antimage/antimage_spell_shield", LUA_MODIFIER_MOTION_NONE) 9 | 10 | function lua_antimage_spell_shield:GetIntrinsicModifierName() 11 | return "modifier_lua_antimage_spell_shield" 12 | end 13 | 14 | function lua_antimage_spell_shield:GetAbilityTextureName() 15 | return "antimage_spell_shield" 16 | end 17 | 18 | function lua_antimage_spell_shield:GetCooldown(level) 19 | if self:GetCaster():HasScepter() then 20 | return self:GetSpecialValueFor("scepter_cooldown") 21 | end 22 | 23 | return 0 24 | end 25 | 26 | 27 | -- Spell shield modifier 28 | modifier_lua_antimage_spell_shield = class({}) 29 | 30 | function modifier_lua_antimage_spell_shield:IsHidden() return true end 31 | function modifier_lua_antimage_spell_shield:IsDebuff() return false end 32 | 33 | function modifier_lua_antimage_spell_shield:OnCreated() 34 | -- Ability properties 35 | self.caster = self:GetCaster() 36 | self.ability = self:GetAbility() 37 | 38 | -- Ability specials 39 | self.spell_shield_resistance = self.ability:GetSpecialValueFor("spell_shield_resistance") 40 | 41 | -- Initialize table of old spells 42 | self.caster.tOldSpells = {} 43 | 44 | if IsServer() then 45 | -- Think, in order to check for scepter. Once a scepter is found, the modifier's interval starts 46 | self:StartIntervalThink(20) 47 | end 48 | end 49 | 50 | function modifier_lua_antimage_spell_shield:OnRefresh() 51 | -- Refresh specials 52 | self.spell_shield_resistance = self.ability:GetSpecialValueFor("spell_shield_resistance") 53 | end 54 | 55 | -- Biggest thanks to Yunten and AtroCty 56 | function modifier_lua_antimage_spell_shield:OnIntervalThink() 57 | if IsServer() then 58 | -- Continually checks if the caster has a scepter. If it does, thinking becomes much faster. 59 | if self.caster:HasScepter() then 60 | 61 | -- Deleting old abilities 62 | for i=#self.caster.tOldSpells,1,-1 do 63 | local hSpell = self.caster.tOldSpells[i] 64 | if hSpell:NumModifiersUsingAbility() == 0 and not hSpell:IsChanneling() then 65 | hSpell:RemoveSelf() 66 | table.remove(self.caster.tOldSpells,i) 67 | end 68 | end 69 | 70 | self:StartIntervalThink(0.2) 71 | else 72 | self:StartIntervalThink(20) 73 | end 74 | end 75 | end 76 | 77 | function modifier_lua_antimage_spell_shield:DeclareFunctions() 78 | local decFuncs = { 79 | MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS, 80 | MODIFIER_PROPERTY_ABSORB_SPELL, 81 | MODIFIER_PROPERTY_REFLECT_SPELL 82 | } 83 | return decFuncs 84 | end 85 | 86 | function modifier_lua_antimage_spell_shield:GetModifierMagicalResistanceBonus() 87 | if not self.caster:PassivesDisabled() then 88 | return self.spell_shield_resistance 89 | end 90 | end 91 | 92 | function modifier_lua_antimage_spell_shield:GetAbsorbSpell( params ) 93 | if IsServer() then 94 | if self.caster:HasScepter() and self.caster:IsRealHero() and self.ability:IsCooldownReady() then 95 | if not self.caster:PassivesDisabled() then 96 | -- Apply Spell Absorption 97 | local reflect_pfx = ParticleManager:CreateParticle("particles/units/heroes/hero_antimage/antimage_spellshield.vpcf", PATTACH_CUSTOMORIGIN_FOLLOW, self.caster) 98 | ParticleManager:SetParticleControlEnt(reflect_pfx, 0, self.caster, PATTACH_POINT_FOLLOW, "attach_hitloc", self.caster:GetOrigin(), true) 99 | ParticleManager:ReleaseParticleIndex(reflect_pfx) 100 | return true 101 | end 102 | end 103 | return false 104 | end 105 | end 106 | 107 | function modifier_lua_antimage_spell_shield:GetReflectSpell( params ) 108 | if IsServer() then 109 | if self.caster:HasScepter() and self.caster:IsRealHero() and self.ability:IsCooldownReady() then 110 | if not self.caster:PassivesDisabled() then 111 | 112 | -- Set ability on cooldown 113 | self.ability:UseResources(false, false, true) 114 | 115 | -- If some spells shouldn't be reflected, enter it into this spell-list 116 | local exception_spell = 117 | { 118 | ["rubick_spell_steal"] = true, 119 | } 120 | 121 | local reflected_spell_name = params.ability:GetAbilityName() 122 | local target = params.ability:GetCaster() 123 | 124 | -- Does not reflect allies' projectiles for any reason 125 | if target:GetTeamNumber() == self.caster:GetTeamNumber() then 126 | return nil 127 | end 128 | 129 | -- Do not reflect spells if the target has Lotus Orb on, otherwise the game will die hard. 130 | if target:HasModifier("modifier_item_lotus_orb_active") then 131 | return nil 132 | end 133 | 134 | if ( not exception_spell[reflected_spell_name] ) and (not target:HasModifier("modifier_lua_antimage_spell_shield")) then 135 | 136 | -- If this is a reflected ability, do nothing 137 | if params.ability.spell_shield_reflect then 138 | return nil 139 | end 140 | 141 | local reflect_pfx = ParticleManager:CreateParticle("particles/units/heroes/hero_antimage/antimage_spellshield_reflect.vpcf", PATTACH_CUSTOMORIGIN_FOLLOW, self.caster) 142 | ParticleManager:SetParticleControlEnt(reflect_pfx, 0, self.caster, PATTACH_POINT_FOLLOW, "attach_hitloc", self.caster:GetAbsOrigin(), true) 143 | ParticleManager:ReleaseParticleIndex(reflect_pfx) 144 | 145 | local old_spell = false 146 | for _,hSpell in pairs(self.caster.tOldSpells) do 147 | if hSpell ~= nil and hSpell:GetAbilityName() == reflected_spell_name then 148 | old_spell = true 149 | break 150 | end 151 | end 152 | 153 | if old_spell then 154 | ability = self.caster:FindAbilityByName(reflected_spell_name) 155 | else 156 | ability = self.caster:AddAbility(reflected_spell_name) 157 | ability:SetStolen(true) 158 | ability:SetHidden(true) 159 | 160 | -- Tag ability as a reflection ability 161 | ability.spell_shield_reflect = true 162 | 163 | -- Modifier counter, and add it into the old-spell list 164 | ability:SetRefCountsModifiers(true) 165 | table.insert(self.caster.tOldSpells, ability) 166 | end 167 | 168 | ability:SetLevel(params.ability:GetLevel()) 169 | -- Set target & fire spell 170 | self.caster:SetCursorCastTarget(target) 171 | ability:OnSpellStart() 172 | target:EmitSound("Hero_Antimage.SpellShield.Reflect") 173 | end 174 | 175 | return false 176 | end 177 | end 178 | end 179 | end 180 | 181 | 182 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/divided_we_stand.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_meepo_divided_we_stand_lua","heroes/meepo/divided_we_stand.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_meepo_divided_we_stand_death_lua","heroes/meepo/divided_we_stand.lua",LUA_MODIFIER_MOTION_NONE) 3 | meepo_divided_we_stand_lua = class({}) 4 | function meepo_divided_we_stand_lua.constructor(self) 5 | self.isScepterUpgraded = false 6 | end 7 | function meepo_divided_we_stand_lua.OnUpgrade(self) 8 | local caster = self.GetCaster(self) 9 | local PID = caster.GetPlayerOwnerID(caster) 10 | local mainMeepo = PlayerResource.GetSelectedHeroEntity(PlayerResource,PID) 11 | local list = mainMeepo.meepoList or {} 12 | if caster~=mainMeepo then 13 | return nil 14 | end 15 | if not mainMeepo.meepoList then 16 | table.insert(list, mainMeepo) 17 | mainMeepo.meepoList=list 18 | mainMeepo.AddNewModifier(mainMeepo,mainMeepo,self,"modifier_meepo_divided_we_stand_lua",{}) 19 | end 20 | local newMeepo = CreateUnitByName(caster.GetUnitName(caster),mainMeepo.GetAbsOrigin(mainMeepo),true,mainMeepo,mainMeepo.GetPlayerOwner(mainMeepo),mainMeepo.GetTeamNumber(mainMeepo)) 21 | newMeepo.SetControllableByPlayer(newMeepo,PID,false) 22 | newMeepo.SetOwner(newMeepo,caster.GetOwner(caster)) 23 | newMeepo.AddNewModifier(newMeepo,mainMeepo,self,"modifier_phased",{["duration"]=0.1}) 24 | local ability = newMeepo.FindAbilityByName(newMeepo,self.GetAbilityName(self)) 25 | newMeepo.AddNewModifier(newMeepo,mainMeepo,self,"modifier_meepo_divided_we_stand_lua",{}) 26 | list=mainMeepo.meepoList 27 | table.insert(list, newMeepo) 28 | mainMeepo.meepoList=list 29 | end 30 | function meepo_divided_we_stand_lua.OnInventoryContentsChanged(self) 31 | if not self.isScepterUpgraded and self.GetCaster(self).HasScepter(self.GetCaster(self)) then 32 | for i=0,5,1 do 33 | local item = self.GetCaster(self).GetItemInSlot(self.GetCaster(self),i) 34 | if item and (item.GetAbilityName(item)=="item_ultimate_scepter") then 35 | item.SetDroppable(item,false) 36 | item.SetSellable(item,false) 37 | item.SetCanBeUsedOutOfInventory(item,false) 38 | self.isScepterUpgraded=true 39 | end 40 | end 41 | self.OnUpgrade(self) 42 | end 43 | end 44 | modifier_meepo_divided_we_stand_lua = class({}) 45 | function modifier_meepo_divided_we_stand_lua.IsHidden(self) 46 | return true 47 | end 48 | function modifier_meepo_divided_we_stand_lua.IsPermanent(self) 49 | return true 50 | end 51 | function modifier_meepo_divided_we_stand_lua.DeclareFunctions(self) 52 | return {MODIFIER_EVENT_ON_ORDER,MODIFIER_EVENT_ON_DEATH,MODIFIER_EVENT_ON_RESPAWN,MODIFIER_EVENT_ON_TAKEDAMAGE} 53 | end 54 | function modifier_meepo_divided_we_stand_lua.OnCreated(self) 55 | if IsServer() then 56 | self.StartIntervalThink(self,FrameTime()) 57 | end 58 | end 59 | function modifier_meepo_divided_we_stand_lua.OnIntervalThink(self) 60 | local meepo = self.GetParent(self) 61 | local mainMeepo = self.GetCaster(self) 62 | local ability = self.GetAbility(self) 63 | if mainMeepo~=meepo then 64 | local boots = {"item_travel_boots2","item_travel_boots","item_guardian_greaves","item_power_treads","item_arcane_boots","item_phase_boots","item_tranquil_boots","item_boots"} 65 | local item = "" 66 | for _, name in pairs(boots) do 67 | if item=="" then 68 | for j=0,5,1 do 69 | local it = mainMeepo.GetItemInSlot(mainMeepo,j) 70 | if it and (name==it.GetAbilityName(it)) then 71 | item=name 72 | end 73 | end 74 | else 75 | break 76 | end 77 | end 78 | if item~="" then 79 | if meepo["item"] then 80 | if meepo["item"]~=item then 81 | UTIL_Remove(meepo["itemHandle"]) 82 | local itemHandle = meepo.AddItemByName(meepo,item) 83 | itemHandle.SetDroppable(itemHandle,false) 84 | itemHandle.SetSellable(itemHandle,false) 85 | itemHandle.SetCanBeUsedOutOfInventory(itemHandle,false) 86 | meepo["itemHandle"]=itemHandle 87 | meepo["item"]=item 88 | end 89 | else 90 | meepo["itemHandle"]=meepo.AddItemByName(meepo,item) 91 | meepo["item"]=item 92 | end 93 | end 94 | for j=0,5,1 do 95 | local itemToCheck = meepo.GetItemInSlot(meepo,j) 96 | if itemToCheck then 97 | local name = itemToCheck.GetAbilityName(itemToCheck) 98 | if name~=item then 99 | UTIL_Remove(itemToCheck) 100 | end 101 | end 102 | end 103 | meepo.SetBaseStrength(meepo,mainMeepo.GetStrength(mainMeepo)) 104 | meepo.SetBaseAgility(meepo,mainMeepo.GetAgility(mainMeepo)) 105 | meepo.SetBaseIntellect(meepo,mainMeepo.GetIntellect(mainMeepo)) 106 | meepo.CalculateStatBonus(meepo) 107 | while meepo.GetLevel(meepo)cloneAbility.GetLevel(cloneAbility) then 185 | cloneAbility.SetLevel(cloneAbility,ability.GetLevel(ability)) 186 | meepo.SetAbilityPoints(meepo,meepo.GetAbilityPoints(meepo)-1) 187 | end 188 | if ability.GetLevel(ability)this.GetCaster() 15 | let PID = caster.GetPlayerOwnerID() 16 | let mainMeepo = PlayerResource.GetSelectedHeroEntity(PID) 17 | let list:CDOTA_BaseNPC_Hero[] = mainMeepo.meepoList || [] 18 | if (caster != mainMeepo) { 19 | return null 20 | } 21 | if (!mainMeepo.meepoList) { 22 | list.push(mainMeepo) 23 | mainMeepo.meepoList = list 24 | mainMeepo.AddNewModifier(mainMeepo,this,"modifier_meepo_divided_we_stand_lua",{}) 25 | } 26 | 27 | let newMeepo = CreateUnitByName( 28 | caster.GetUnitName(), 29 | mainMeepo.GetAbsOrigin(), 30 | true,mainMeepo, 31 | mainMeepo.GetPlayerOwner(), 32 | mainMeepo.GetTeamNumber() 33 | ) 34 | newMeepo.SetControllableByPlayer(PID,false) 35 | newMeepo.SetOwner(caster.GetOwner()) 36 | newMeepo.AddNewModifier(mainMeepo,this,"modifier_phased",{duration:0.1}) 37 | let ability = newMeepo.FindAbilityByName(this.GetAbilityName()) 38 | newMeepo.AddNewModifier(mainMeepo,this,"modifier_meepo_divided_we_stand_lua",{}) 39 | list = mainMeepo.meepoList 40 | list.push(newMeepo) 41 | mainMeepo.meepoList = list 42 | } 43 | 44 | OnInventoryContentsChanged() { 45 | // The scepter part 46 | if (!this.isScepterUpgraded && this.GetCaster().HasScepter()) { 47 | for (let i=0 ; i<=5 ; i++) { 48 | let item:CDOTA_Item = this.GetCaster().GetItemInSlot(i) 49 | if (item && item.GetAbilityName() == "item_ultimate_scepter") { 50 | item.SetDroppable(false) 51 | item.SetSellable(false) 52 | item.SetCanBeUsedOutOfInventory(false) 53 | this.isScepterUpgraded = true 54 | } 55 | } 56 | this.OnUpgrade() 57 | } 58 | } 59 | } 60 | 61 | // Modifier stuff 62 | class modifier_meepo_divided_we_stand_lua extends CDOTA_Modifier_Lua { 63 | IsHidden() {return true} 64 | IsPermanent() {return true} 65 | boots_list:string[] 66 | 67 | DeclareFunctions() { 68 | return [ 69 | modifierfunction.MODIFIER_EVENT_ON_ORDER, 70 | modifierfunction.MODIFIER_EVENT_ON_DEATH, 71 | modifierfunction.MODIFIER_EVENT_ON_RESPAWN, 72 | modifierfunction.MODIFIER_EVENT_ON_TAKEDAMAGE, 73 | ] 74 | } 75 | 76 | // Stats sync with client on refresh and no other syncing needed.(?) 77 | OnCreated() { 78 | if (IsServer()) { 79 | this.StartIntervalThink(FrameTime()) 80 | } 81 | } 82 | 83 | OnIntervalThink() { 84 | let meepo = this.GetParent() 85 | let mainMeepo = this.GetCaster() 86 | let ability: CDOTABaseAbility = this.GetAbility() 87 | // Loop through abilities to sync levels 88 | if (mainMeepo != meepo) { 89 | let boots:string[] = [ 90 | "item_travel_boots2", 91 | "item_travel_boots", 92 | "item_guardian_greaves", 93 | "item_power_treads", 94 | "item_arcane_boots", 95 | "item_phase_boots", 96 | "item_tranquil_boots", 97 | "item_boots" 98 | ] 99 | // Get the highest ranked boots 100 | let item = "" 101 | for (let name of boots) { 102 | if (item == "") { 103 | for (let j=0;j<=5;j++) { 104 | let it = mainMeepo.GetItemInSlot(j) 105 | if (it && name == it.GetAbilityName()) { 106 | item = name 107 | } 108 | } 109 | } else { 110 | break 111 | } 112 | } 113 | // Remove previous item if it exists and isn't the same 114 | // Add the best boots 115 | if (item != "") { 116 | if (meepo["item"]) { 117 | if (meepo["item"] != item) { 118 | UTIL_Remove(meepo["itemHandle"]) 119 | let itemHandle = meepo.AddItemByName(item) 120 | itemHandle.SetDroppable(false) 121 | itemHandle.SetSellable(false) 122 | itemHandle.SetCanBeUsedOutOfInventory(false) 123 | meepo["itemHandle"] = itemHandle 124 | meepo["item"] = item 125 | } 126 | } else { 127 | meepo["itemHandle"] = meepo.AddItemByName(item) 128 | meepo["item"] = item 129 | } 130 | 131 | // Remove all items from clones 132 | } 133 | for (let j=0;j<=5;j++) { 134 | let itemToCheck:CDOTA_Item = meepo.GetItemInSlot(j) 135 | if (itemToCheck) { 136 | let name = itemToCheck.GetAbilityName() 137 | if (name != item) { 138 | UTIL_Remove(itemToCheck) 139 | } 140 | } 141 | } 142 | // Set base stats equal to main meepo stats 143 | meepo.SetBaseStrength(mainMeepo.GetStrength()) 144 | meepo.SetBaseAgility(mainMeepo.GetAgility()) 145 | meepo.SetBaseIntellect(mainMeepo.GetIntellect()) 146 | meepo.CalculateStatBonus() 147 | 148 | while(meepo.GetLevel() < mainMeepo.GetLevel()) { 149 | meepo.AddExperience(10,1,false,false) 150 | } 151 | } else { 152 | LevelAbilitiesForAllMeepos(meepo) 153 | } 154 | } 155 | // Use this to only kill the main meepo and hide the others 156 | OnTakeDamage(keys:ModifierAttackEvent) { 157 | let mainMeepo = this.GetCaster() 158 | let parent = this.GetParent() 159 | if (keys.unit == parent) { 160 | if (parent.GetHealth() < 0) { 161 | if (parent != this.GetCaster()) { 162 | // Move the main hero to this location to grant exp etc, then move back in case of aegis/bloodstone 163 | let oldLocation = mainMeepo.GetAbsOrigin() 164 | mainMeepo.SetAbsOrigin(parent.GetAbsOrigin()) 165 | mainMeepo.Kill(keys.inflictor,keys.attacker) 166 | mainMeepo.SetAbsOrigin(oldLocation) 167 | // Make sure the hero survives this 168 | parent.SetHealth(parent.GetMaxHealth()) 169 | } 170 | for (let meepo of GetAllMeepos(mainMeepo)) { 171 | if (meepo != mainMeepo) { 172 | meepo.AddNewModifier(mainMeepo,this.GetAbility(),"modifier_meepo_divided_we_stand_death_lua",{}) 173 | } 174 | } 175 | } 176 | } 177 | } 178 | OnRespawn(keys:ModifierEvent) { 179 | let parent = this.GetParent() 180 | let mainMeepo = this.GetCaster() 181 | if (keys.unit == parent && parent == PlayerResource.GetSelectedHeroEntity(this.GetParent().GetPlayerOwnerID())) { 182 | for (let meepo of GetAllMeepos(mainMeepo)) { 183 | if (meepo != mainMeepo) { 184 | meepo.RemoveModifierByName("modifier_meepo_divided_we_stand_death_lua") 185 | meepo.RemoveNoDraw() 186 | FindClearSpaceForUnit(meepo,this.GetParent().GetAbsOrigin(),true) 187 | meepo.AddNewModifier(meepo, this.GetAbility(), "modifier_phased", {duration:0.1}) 188 | } 189 | } 190 | } 191 | } 192 | } 193 | // Use this to fake deaths 194 | class modifier_meepo_divided_we_stand_death_lua extends CDOTA_Modifier_Lua { 195 | IsPermanent() { return false} 196 | IsHidden() { return true } 197 | 198 | OnCreated() { 199 | if (IsServer()) { 200 | this.GetParent().StartGesture(GameActivity_t.ACT_DOTA_DIE) 201 | this.StartIntervalThink(1.5) 202 | } 203 | } 204 | 205 | OnIntervalThink() { 206 | let parent = this.GetParent() 207 | parent.RemoveGesture(GameActivity_t.ACT_DOTA_DIE) 208 | parent.AddNoDraw() 209 | if (parent.GetTeamNumber() == DOTATeam_t.DOTA_TEAM_GOODGUYS) { 210 | parent.SetAbsOrigin(Vector(-10000,-10000,0)) 211 | } else { 212 | parent.SetAbsOrigin(Vector(10000,10000,0)) 213 | } 214 | this.StartIntervalThink(-1) 215 | } 216 | 217 | CheckState() { 218 | return { 219 | [modifierstate.MODIFIER_STATE_STUNNED]: true, 220 | [modifierstate.MODIFIER_STATE_UNSELECTABLE]:true, 221 | [modifierstate.MODIFIER_STATE_INVULNERABLE]:true, 222 | [modifierstate.MODIFIER_STATE_UNTARGETABLE]:true, 223 | [modifierstate.MODIFIER_STATE_OUT_OF_GAME]:true, 224 | [modifierstate.MODIFIER_STATE_NO_HEALTH_BAR]:true, 225 | [modifierstate.MODIFIER_STATE_NOT_ON_MINIMAP]:true, 226 | } 227 | } 228 | } 229 | 230 | // LevelAbilityForAllMeepos 231 | // This can't be done in the onupgrade blocks because of talents 232 | function LevelAbilitiesForAllMeepos(caster:CDOTA_BaseNPC_Hero): void { 233 | let PID = caster.GetPlayerOwnerID() 234 | let mainMeepo = PlayerResource.GetSelectedHeroEntity(PID) 235 | if (caster == mainMeepo) { 236 | for (let a=0;a <= caster.GetAbilityCount()-1;a++) { 237 | let ability: CDOTABaseAbility = caster.GetAbilityByIndex(a) 238 | if (ability) { 239 | for (let meepo of GetAllMeepos(mainMeepo)) { 240 | let cloneAbility = meepo.FindAbilityByName(ability.GetAbilityName()) 241 | if (ability.GetLevel() > cloneAbility.GetLevel()) { 242 | cloneAbility.SetLevel(ability.GetLevel()) 243 | meepo.SetAbilityPoints(meepo.GetAbilityPoints()-1) 244 | } 245 | if (ability.GetLevel() < cloneAbility.GetLevel()) { 246 | ability.SetLevel(cloneAbility.GetLevel()) 247 | mainMeepo.SetAbilityPoints(mainMeepo.GetAbilityPoints()-1) 248 | } 249 | } 250 | } 251 | } 252 | } 253 | } 254 | 255 | function GetAllMeepos(caster:CDOTA_BaseNPC_Hero):CDOTA_BaseNPC_Hero[] { 256 | if (caster.meepoList) { 257 | return caster.meepoList 258 | } else { 259 | return [caster] 260 | } 261 | } 262 | 263 | function MeepoExperience(filterTable:table): table { 264 | let PID = filterTable.player_id_const 265 | let reason = filterTable.reason_const 266 | let experience = filterTable.experience 267 | let hero = PlayerResource.GetSelectedHeroEntity(PID) 268 | if (hero && hero.HasAbility("meepo_divided_we_stand_lua") && reason != EDOTA_ModifyXP_Reason.DOTA_ModifyXP_Unspecified) { 269 | filterTable.experience = 0 270 | hero.AddExperience(experience,EDOTA_ModifyXP_Reason.DOTA_ModifyXP_Unspecified,true,false) 271 | } 272 | return filterTable 273 | } 274 | 275 | function MeepoOrderFilter(filterTable:table):boolean { 276 | let entindex_ability = filterTable.entindex_ability 277 | let sequence_number_const = filterTable.sequence_number_const 278 | let queue = <0|1>filterTable.sequence_number_const 279 | let units = filterTable.units 280 | let entindex_target = filterTable.entindex_target 281 | let position = Vector(filterTable.position_y,filterTable.position_y,filterTable.position_z) 282 | let order_type = filterTable.order_type 283 | let issuer_player_id_const = filterTable.issuer_player_id_const 284 | let ability = EntIndexToHScript(entindex_ability) 285 | let target = EntIndexToHScript(entindex_target) 286 | for (let entindex_unit of units) { 287 | let unit = EntIndexToHScript(entindex_unit) 288 | // Cancel picking up items as clone 289 | if (unit.HasModifier("modifier_meepo_divided_we_stand_lua") && unit != PlayerResource.GetSelectedHeroEntity(unit.GetPlayerOwnerID())) { 290 | if (order_type == DotaUnitOrder_t.DOTA_UNIT_ORDER_PICKUP_ITEM) { 291 | return false 292 | } 293 | } 294 | // Prevent giving items to meepo 295 | if (target && target.HasModifier && target.HasModifier("modifier_meepo_divided_we_stand_lua") && target != PlayerResource.GetSelectedHeroEntity(target.GetPlayerOwnerID())) { 296 | if (order_type == DotaUnitOrder_t.DOTA_UNIT_ORDER_GIVE_ITEM) { 297 | return false 298 | } 299 | } 300 | } 301 | return true 302 | } 303 | 304 | if (IsServer()) { 305 | // If you are using an order filter yourself already, copy the meepo related line in there 306 | GameRules.GetGameModeEntity().SetExecuteOrderFilter( 307 | function(filterTable):boolean { 308 | //let meepo = require('heroes/meepo/divided_we_stand') 309 | if (MeepoOrderFilter) { 310 | if (!MeepoOrderFilter(filterTable)) { 311 | return false 312 | } 313 | } 314 | return true 315 | }, 316 | GameRules.GetGameModeEntity() 317 | ) 318 | // If you are using an experience filter yourself already, copy the meepo related line in there 319 | GameRules.GetGameModeEntity().SetModifyExperienceFilter( 320 | function(filterTable):boolean { 321 | //let meepo = require('heroes/meepo/divided_we_stand') 322 | if (MeepoExperience) { 323 | filterTable = MeepoExperience(filterTable) 324 | } 325 | return true 326 | }, 327 | GameRules.GetGameModeEntity() 328 | ) 329 | } -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/earthbind.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_meepo_earthbind_lua","heroes/meepo/earthbind.lua",LUA_MODIFIER_MOTION_NONE) 2 | meepo_earthbind_lua = class({}) 3 | function meepo_earthbind_lua.GetCooldown(self) 4 | local caster = self.GetCaster(self) 5 | local cooldown = self.GetSpecialValueFor(self,"cooldown") 6 | if IsServer() then 7 | local talent = self.GetCaster(self).FindAbilityByName(self.GetCaster(self),"special_bonus_unique_meepo_3") 8 | if talent then 9 | cooldown=cooldown-talent.GetSpecialValueFor(talent,"value") 10 | end 11 | end 12 | return cooldown 13 | end 14 | function meepo_earthbind_lua.OnAbilityPhaseStart(self) 15 | self.GetCaster(self).EmitSound(self.GetCaster(self),"Hero_Meepo.Earthbind.Cast") 16 | return true 17 | end 18 | function meepo_earthbind_lua.OnAbilityPhaseInterrupted(self) 19 | self.GetCaster(self).StopSound(self.GetCaster(self),"Hero_Meepo.Earthbind.Cast") 20 | end 21 | function meepo_earthbind_lua.OnSpellStart(self) 22 | local caster = self.GetCaster(self) 23 | local point = self.GetCursorPosition(self) 24 | local projectileSpeed = self.GetSpecialValueFor(self,"speed") 25 | local direction = point-caster.GetAbsOrigin(caster) 26 | direction=direction.Normalized(direction) 27 | direction[2]=0 28 | direction=(direction*projectileSpeed) 29 | local range = point-caster.GetAbsOrigin(caster) 30 | range=range.Length2D(range) 31 | local radius = self.GetSpecialValueFor(self,"radius") 32 | self.particle=ParticleManager.CreateParticle(ParticleManager,"particles/units/heroes/hero_meepo/meepo_earthbind_projectile_fx.vpcf",PATTACH_ABSORIGIN_FOLLOW,caster) 33 | ParticleManager.SetParticleControl(ParticleManager,self.particle,0,caster.GetAbsOrigin(caster)) 34 | ParticleManager.SetParticleControl(ParticleManager,self.particle,1,point) 35 | ParticleManager.SetParticleControl(ParticleManager,self.particle,2,Vector(projectileSpeed,0,0)) 36 | ParticleManager.SetParticleControl(ParticleManager,self.particle,3,point) 37 | local projectileTable = {["Ability"]=self,["EffectName"]="",["vSpawnOrigin"]=caster.GetAbsOrigin(caster),["fDistance"]=range,["fStartRadius"]=radius,["fEndRadius"]=radius,["Source"]=caster,["bHasFrontalCone"]=false,["bReplaceExisting"]=false,["iUnitTargetTeam"]=DOTA_UNIT_TARGET_TEAM_NONE,["iUnitTargetFlags"]=DOTA_UNIT_TARGET_FLAG_NONE,["iUnitTargetType"]=DOTA_UNIT_TARGET_NONE,["fExpireTime"]=(GameRules.GetGameTime(GameRules)+0.25)+(range/projectileSpeed),["bDeleteOnHit"]=false,["vVelocity"]=direction,["bProvidesVision"]=true,["iVisionRadius"]=radius,["iVisionTeamNumber"]=caster.GetTeamNumber(caster)} 38 | ProjectileManager.CreateLinearProjectile(ProjectileManager,projectileTable) 39 | end 40 | function meepo_earthbind_lua.OnProjectileHit(self,target,location) 41 | local caster = self.GetCaster(self) 42 | local duration = self.GetSpecialValueFor(self,"duration") 43 | local radius = self.GetSpecialValueFor(self,"radius") 44 | local units = FindUnitsInRadius(caster.GetTeamNumber(caster),location,nil,radius,DOTA_UNIT_TARGET_TEAM_ENEMY,DOTA_UNIT_TARGET_BASIC+DOTA_UNIT_TARGET_HERO,DOTA_UNIT_TARGET_FLAG_NONE,0,false) 45 | for _, unit in pairs(units) do 46 | unit.AddNewModifier(unit,caster,self,"modifier_meepo_earthbind_lua",{["duration"]=duration}) 47 | unit.EmitSound(unit,"Hero_Meepo.Earthbind.Target") 48 | end 49 | ParticleManager.DestroyParticle(ParticleManager,self.particle,false) 50 | ParticleManager.ReleaseParticleIndex(ParticleManager,self.particle) 51 | return true 52 | end 53 | modifier_meepo_earthbind_lua = class({}) 54 | function modifier_meepo_earthbind_lua.GetPriority(self) 55 | return MODIFIER_PRIORITY_HIGH 56 | end 57 | function modifier_meepo_earthbind_lua.CheckState(self) 58 | local funcs = {[MODIFIER_STATE_INVISIBLE]=false,[MODIFIER_STATE_ROOTED]=true} 59 | return funcs 60 | end 61 | function modifier_meepo_earthbind_lua.GetEffectName(self) 62 | return "particles/units/heroes/hero_meepo/meepo_earthbind.vpcf" 63 | end 64 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/earthbind.ts: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_meepo_earthbind_lua","heroes/meepo/earthbind.lua",LuaModifierType.LUA_MODIFIER_MOTION_NONE) 2 | 3 | class meepo_earthbind_lua extends CDOTA_Ability_Lua { 4 | particle:ParticleID 5 | 6 | GetCooldown() { 7 | let caster:CDOTA_BaseNPC = this.GetCaster() 8 | let cooldown = this.GetSpecialValueFor("cooldown") 9 | if (IsServer()) { 10 | let talent:CDOTABaseAbility = this.GetCaster().FindAbilityByName("special_bonus_unique_meepo_3") 11 | if (talent) { 12 | cooldown -= talent.GetSpecialValueFor("value") 13 | } 14 | } 15 | return cooldown 16 | } 17 | 18 | OnAbilityPhaseStart() { 19 | this.GetCaster().EmitSound("Hero_Meepo.Earthbind.Cast") 20 | return true 21 | } 22 | 23 | OnAbilityPhaseInterrupted() { 24 | this.GetCaster().StopSound("Hero_Meepo.Earthbind.Cast") 25 | } 26 | 27 | OnSpellStart() { 28 | let caster = this.GetCaster() 29 | let point = this.GetCursorPosition() 30 | let projectileSpeed = this.GetSpecialValueFor("speed") 31 | let direction = point-caster.GetAbsOrigin() 32 | direction = direction.Normalized() 33 | direction[2] = 0 34 | direction = direction * projectileSpeed 35 | let range = point - caster.GetAbsOrigin() 36 | range = range.Length2D() 37 | let radius = this.GetSpecialValueFor("radius") 38 | this.particle = ParticleManager.CreateParticle("particles/units/heroes/hero_meepo/meepo_earthbind_projectile_fx.vpcf", ParticleAttachment_t.PATTACH_ABSORIGIN_FOLLOW, caster) 39 | ParticleManager.SetParticleControl(this.particle, 0, caster.GetAbsOrigin()) 40 | ParticleManager.SetParticleControl(this.particle, 1, point) 41 | ParticleManager.SetParticleControl(this.particle, 2, Vector(projectileSpeed, 0, 0)) 42 | ParticleManager.SetParticleControl(this.particle, 3, point) 43 | 44 | let projectileTable:LinearProjectileTable = { 45 | Ability: this, 46 | EffectName: "", 47 | vSpawnOrigin: caster.GetAbsOrigin(), 48 | fDistance: range, 49 | fStartRadius: radius, 50 | fEndRadius: radius, 51 | Source: caster, 52 | bHasFrontalCone: false, 53 | bReplaceExisting: false, 54 | iUnitTargetTeam: DOTA_UNIT_TARGET_TEAM.DOTA_UNIT_TARGET_TEAM_NONE, 55 | iUnitTargetFlags: DOTA_UNIT_TARGET_FLAGS.DOTA_UNIT_TARGET_FLAG_NONE, 56 | iUnitTargetType: DOTA_UNIT_TARGET_TYPE.DOTA_UNIT_TARGET_NONE, 57 | fExpireTime: GameRules.GetGameTime()+0.25+range/projectileSpeed , 58 | bDeleteOnHit: false, 59 | vVelocity: direction, 60 | bProvidesVision: true, 61 | iVisionRadius: radius, 62 | iVisionTeamNumber: caster.GetTeamNumber(), 63 | } 64 | ProjectileManager.CreateLinearProjectile(projectileTable) 65 | } 66 | 67 | OnProjectileHit(target:CDOTA_BaseNPC,location:Vec) { 68 | let caster = this.GetCaster() 69 | let duration = this.GetSpecialValueFor("duration") 70 | let radius = this.GetSpecialValueFor("radius") 71 | let units = FindUnitsInRadius( 72 | caster.GetTeamNumber(), 73 | location, 74 | null, 75 | radius, 76 | DOTA_UNIT_TARGET_TEAM.DOTA_UNIT_TARGET_TEAM_ENEMY, 77 | DOTA_UNIT_TARGET_TYPE.DOTA_UNIT_TARGET_BASIC+DOTA_UNIT_TARGET_TYPE.DOTA_UNIT_TARGET_HERO, 78 | DOTA_UNIT_TARGET_FLAGS.DOTA_UNIT_TARGET_FLAG_NONE, 79 | 0, 80 | false 81 | ) 82 | for (let unit of units) { 83 | unit.AddNewModifier(caster,this,"modifier_meepo_earthbind_lua",{duration:duration}) 84 | unit.EmitSound("Hero_Meepo.Earthbind.Target") 85 | } 86 | ParticleManager.DestroyParticle(this.particle,false) 87 | ParticleManager.ReleaseParticleIndex(this.particle) 88 | return true 89 | } 90 | } 91 | 92 | class modifier_meepo_earthbind_lua extends CDOTA_Modifier_Lua { 93 | // Override invis 94 | GetPriority() { 95 | return modifierpriority.MODIFIER_PRIORITY_HIGH 96 | } 97 | 98 | CheckState() { 99 | let funcs = { 100 | [modifierstate.MODIFIER_STATE_INVISIBLE]: false, 101 | [modifierstate.MODIFIER_STATE_ROOTED]: true, 102 | } 103 | return funcs 104 | } 105 | 106 | GetEffectName() { 107 | return "particles/units/heroes/hero_meepo/meepo_earthbind.vpcf" 108 | } 109 | } -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/geostrike.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_meepo_geostrike_lua","heroes/meepo/geostrike.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_meepo_geostrike_debuff_lua","heroes/meepo/geostrike.lua",LUA_MODIFIER_MOTION_NONE) 3 | meepo_geostrike_lua = class({}) 4 | function meepo_geostrike_lua.GetIntrinsicModifierName(self) 5 | return "modifier_meepo_geostrike_lua" 6 | end 7 | modifier_meepo_geostrike_lua = class({}) 8 | function modifier_meepo_geostrike_lua.IsHidden(self) 9 | return true 10 | end 11 | function modifier_meepo_geostrike_lua.IsPermanent(self) 12 | return true 13 | end 14 | function modifier_meepo_geostrike_lua.DeclareFunctions(self) 15 | return {MODIFIER_EVENT_ON_ATTACK_LANDED} 16 | end 17 | function modifier_meepo_geostrike_lua.OnAttackLanded(self,keys) 18 | if (keys.attacker==self.GetParent(self)) and not keys.attacker.PassivesDisabled(keys.attacker) then 19 | local ability = self.GetAbility(self) 20 | local duration = ability.GetSpecialValueFor(ability,"duration_tooltip") 21 | local target = keys.target 22 | local attacker = keys.attacker 23 | local modifier = target.FindModifierByNameAndCaster(target,"modifier_meepo_geostrike_debuff_lua",attacker) 24 | if modifier then 25 | modifier.SetDuration(modifier,duration,true) 26 | else 27 | target.AddNewModifier(target,attacker,self.GetAbility(self),"modifier_meepo_geostrike_debuff_lua",{["duration"]=duration}) 28 | end 29 | end 30 | end 31 | modifier_meepo_geostrike_debuff_lua = class({}) 32 | function modifier_meepo_geostrike_debuff_lua.GetAttributes(self) 33 | return MODIFIER_ATTRIBUTE_MULTIPLE 34 | end 35 | function modifier_meepo_geostrike_debuff_lua.DeclareFunctions(self) 36 | return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE} 37 | end 38 | function modifier_meepo_geostrike_debuff_lua.GetModifierMoveSpeedBonus_Percentage(self) 39 | return self.slow 40 | end 41 | function modifier_meepo_geostrike_debuff_lua.OnCreated(self) 42 | if IsServer() then 43 | self.damage=self.GetAbility(self).GetAbilityDamage(self.GetAbility(self)) 44 | self.slow=self.GetAbility(self).GetSpecialValueFor(self.GetAbility(self),"slow") 45 | self.StartIntervalThink(self,1) 46 | self.particle=ParticleManager.CreateParticle(ParticleManager,"particles/units/heroes/hero_meepo/meepo_geostrike.vpcf",PATTACH_ABSORIGIN_FOLLOW,self.GetCaster(self)) 47 | ParticleManager.SetParticleControl(ParticleManager,self.particle,0,Vector(0,0,0)) 48 | self.AddParticle(self,self.particle,true,false,1,false,false) 49 | end 50 | end 51 | function modifier_meepo_geostrike_debuff_lua.OnIntervalThink(self) 52 | local dTable = {["attacker"]=self.GetCaster(self),["victim"]=self.GetParent(self),["damage"]=self.damage,["damage_type"]=DAMAGE_TYPE_MAGICAL} 53 | ApplyDamage(dTable) 54 | end 55 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/geostrike.ts: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_meepo_geostrike_lua","heroes/meepo/geostrike.lua",LuaModifierType.LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_meepo_geostrike_debuff_lua","heroes/meepo/geostrike.lua",LuaModifierType.LUA_MODIFIER_MOTION_NONE) 3 | 4 | class meepo_geostrike_lua extends CDOTA_Ability_Lua { 5 | GetIntrinsicModifierName() { return "modifier_meepo_geostrike_lua" } 6 | } 7 | 8 | class modifier_meepo_geostrike_lua extends CDOTA_Modifier_Lua { 9 | IsHidden() {return true} 10 | IsPermanent() {return true} 11 | 12 | DeclareFunctions() { 13 | return [ 14 | modifierfunction.MODIFIER_EVENT_ON_ATTACK_LANDED, 15 | ] 16 | } 17 | 18 | OnAttackLanded(keys:ModifierAttackEvent) { 19 | if (keys.attacker == this.GetParent() && !keys.attacker.PassivesDisabled()) { 20 | let ability = this.GetAbility() 21 | let duration = ability.GetSpecialValueFor("duration_tooltip") 22 | let target = keys.target 23 | let attacker = keys.attacker 24 | let modifier = target.FindModifierByNameAndCaster("modifier_meepo_geostrike_debuff_lua",attacker) 25 | if (modifier) { 26 | modifier.SetDuration(duration,true) 27 | } else { 28 | target.AddNewModifier(attacker,this.GetAbility(),"modifier_meepo_geostrike_debuff_lua",{duration:duration}) 29 | } 30 | } 31 | } 32 | } 33 | 34 | class modifier_meepo_geostrike_debuff_lua extends CDOTA_Modifier_Lua { 35 | damage : number 36 | slow : number 37 | particle:ParticleID 38 | 39 | GetAttributes() { 40 | return DOTAModifierAttribute_t.MODIFIER_ATTRIBUTE_MULTIPLE 41 | } 42 | 43 | DeclareFunctions() { 44 | return [ 45 | modifierfunction.MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 46 | ] 47 | } 48 | 49 | GetModifierMoveSpeedBonus_Percentage() { 50 | return this.slow 51 | } 52 | 53 | OnCreated() { 54 | if (IsServer()) { 55 | this.damage = this.GetAbility().GetAbilityDamage() 56 | this.slow = this.GetAbility().GetSpecialValueFor("slow") 57 | this.StartIntervalThink(1) 58 | } 59 | } 60 | 61 | OnIntervalThink() { 62 | let dTable: DamageTable = { 63 | attacker:this.GetCaster(), 64 | victim:this.GetParent(), 65 | damage:this.damage, 66 | damage_type:DAMAGE_TYPES.DAMAGE_TYPE_MAGICAL 67 | } 68 | ApplyDamage(dTable) 69 | } 70 | 71 | GetEffectName() { 72 | return "particles/units/heroes/hero_meepo/meepo_geostrike.vpcf" 73 | } 74 | 75 | GetEffectAttachType() { 76 | return ParticleAttachment_t.PATTACH_ABSORIGIN_FOLLOW 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/meepo/poof.lua: -------------------------------------------------------------------------------- 1 | require("heroes/meepo/divided_we_stand") 2 | meepo_poof_lua = class({}) 3 | function meepo_poof_lua.CastFilterResultTarget(self,target) 4 | if (target.GetName(target)==self.GetCaster(self).GetName(self.GetCaster(self))) and (target.GetPlayerOwnerID(target)==self.GetCaster(self).GetPlayerOwnerID(self.GetCaster(self))) then 5 | return UF_SUCCESS 6 | else 7 | return UF_FAIL_CUSTOM 8 | end 9 | end 10 | function meepo_poof_lua.GetCustomCastErrorTarget(self,target) 11 | return "#error_meepo_poof" 12 | end 13 | function meepo_poof_lua.GetCooldown(self,iLevel) 14 | local caster = self.GetCaster(self) 15 | local cooldown = self.GetSpecialValueFor(self,"cooldown") 16 | if IsServer() then 17 | local talent = self.GetCaster(self).FindAbilityByName(self.GetCaster(self),"special_bonus_unique_meepo") 18 | if talent then 19 | cooldown=cooldown-talent.GetSpecialValueFor(talent,"value") 20 | end 21 | end 22 | return cooldown 23 | end 24 | function meepo_poof_lua.OnAbilityPhaseStart(self) 25 | self.particle=ParticleManager.CreateParticle(ParticleManager,"particles/units/heroes/hero_meepo/meepo_poof_start.vpcf",PATTACH_ABSORIGIN_FOLLOW,self.GetCaster(self)) 26 | self.GetCaster(self).EmitSound(self.GetCaster(self),"Hero_Meepo.Poof.Channel") 27 | return true 28 | end 29 | function meepo_poof_lua.OnAbilityPhaseInterrupted(self) 30 | ParticleManager.DestroyParticle(ParticleManager,self.particle,false) 31 | self.GetCaster(self).StopSound(self.GetCaster(self),"Hero_Meepo.Poof.Channel") 32 | end 33 | function meepo_poof_lua.OnSpellStart(self) 34 | local caster = self.GetCaster(self) 35 | local caster_origin = caster.GetAbsOrigin(caster) 36 | local target = self.GetCursorTarget(self) 37 | if not target then 38 | local PID = caster.GetPlayerOwnerID(caster) 39 | local mainMeepo = PlayerResource.GetSelectedHeroEntity(PlayerResource,PID) 40 | local dist = 999999 41 | for _, meepo in pairs(GetAllMeepos(mainMeepo)) do 42 | local range = meepo.GetAbsOrigin(meepo)-self.GetCursorPosition(self) 43 | range=range.Length2D(range) 44 | if range 0 then 101 | damage = self.damage_per_instance / #enemies 102 | end 103 | 104 | -- Deal damage to each hero 105 | for _,enemy in pairs (enemies) do 106 | local damageTable = {victim = enemy, 107 | attacker = self.caster, 108 | damage = damage, 109 | damage_type = DAMAGE_TYPE_MAGICAL, 110 | ability = self.ability 111 | } 112 | 113 | ApplyDamage(damageTable) 114 | end 115 | end 116 | end 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/skywrath_mage/skywrath_mage_target_finder.lua: -------------------------------------------------------------------------------- 1 | -- Helper function that finds a target to fire a secondary spell at. Relevant for all Skywrath's spells 2 | -- that has a unit target (or chooses a specific target) 3 | function SkywrathSpellsTargetFinder(caster, target, radius) 4 | 5 | -- Find heroes around the target 6 | local enemies = FindUnitsInRadius(caster:GetTeamNumber(), 7 | target:GetAbsOrigin(), 8 | nil, 9 | radius, 10 | DOTA_UNIT_TARGET_TEAM_ENEMY, 11 | DOTA_UNIT_TARGET_HERO, 12 | DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE + DOTA_UNIT_TARGET_FLAG_NO_INVIS, 13 | FIND_ANY_ORDER, 14 | false) 15 | 16 | -- Cycle for a target that is not the initial target 17 | for _,enemy in pairs(enemies) do 18 | 19 | -- If a valid hero target was found, return it 20 | if enemy ~= target then 21 | return enemy 22 | end 23 | end 24 | 25 | -- If this check failed, check for creeps/creep heroes instead 26 | local enemies = FindUnitsInRadius(caster:GetTeamNumber(), 27 | target:GetAbsOrigin(), 28 | nil, 29 | radius, 30 | DOTA_UNIT_TARGET_TEAM_ENEMY, 31 | DOTA_UNIT_TARGET_BASIC, 32 | DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE + DOTA_UNIT_TARGET_FLAG_NO_INVIS, 33 | FIND_ANY_ORDER, 34 | false) 35 | 36 | -- Cycle for a target that is not the initial target 37 | for _,enemy in pairs(enemies) do 38 | 39 | -- If a valid creep target was found, return it 40 | if enemy ~= target then 41 | return enemy 42 | end 43 | end 44 | 45 | -- Otherwise, return nothing 46 | return nil 47 | end 48 | 49 | -- Helper function that finds a valid target position to fire at. Relevant for Mystic Flare only (point target spell) 50 | function SkywrathSpellsPositionFinder(caster, target_point, radius, min_distance) 51 | -- Find heroes around the target 52 | local enemies = FindUnitsInRadius(caster:GetTeamNumber(), 53 | target_point, 54 | nil, 55 | radius, 56 | DOTA_UNIT_TARGET_TEAM_ENEMY, 57 | DOTA_UNIT_TARGET_HERO, 58 | DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE + DOTA_UNIT_TARGET_FLAG_NO_INVIS, 59 | FIND_ANY_ORDER, 60 | false) 61 | 62 | -- Cycle for a target that isn't in the initial target point area 63 | for _,enemy in pairs(enemies) do 64 | 65 | -- Check distance between found enemy and target point and make sure it's higher than the minimum distance 66 | local distance = (enemy:GetAbsOrigin() - target_point):Length2D() 67 | 68 | -- If a valid hero target was found, return it 69 | if distance > min_distance then 70 | return enemy 71 | end 72 | end 73 | 74 | -- If this check failed, check for creeps/creep heroes instead 75 | local enemies = FindUnitsInRadius(caster:GetTeamNumber(), 76 | target:GetAbsOrigin(), 77 | nil, 78 | radius, 79 | DOTA_UNIT_TARGET_TEAM_ENEMY, 80 | DOTA_UNIT_TARGET_BASIC, 81 | DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE + DOTA_UNIT_TARGET_FLAG_NO_INVIS, 82 | FIND_ANY_ORDER, 83 | false) 84 | 85 | -- Cycle for a target that isn't in the initial target point area 86 | for _,enemy in pairs(enemies) do 87 | 88 | -- Check distance between found enemy and target point and make sure it's higher than the minimum distance 89 | local distance = (enemy:GetAbsOrigin() - target_point):Length2D() 90 | 91 | -- If a valid hero target was found, return it 92 | if distance > min_distance then 93 | return enemy 94 | end 95 | end 96 | 97 | -- Otherwise, return nothing 98 | return nil 99 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/sniper/assassinate.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_sniper_assassinate_caster_lua","heroes/sniper/assassinate.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_sniper_assassinate_target_lua","heroes/sniper/assassinate.lua",LUA_MODIFIER_MOTION_NONE) 3 | 4 | -- The spell is almost completely different with and without Aghanims Scepter. 5 | ---@class sniper_assassinate_lua : CDOTA_Ability_Lua 6 | sniper_assassinate_lua = class({}) 7 | ---@override 8 | function sniper_assassinate_lua:GetBehavior() 9 | if self:GetCaster():HasScepter() then 10 | return DOTA_ABILITY_BEHAVIOR_POINT + DOTA_ABILITY_BEHAVIOR_AOE 11 | else -- Normal (non-scepter) 12 | return DOTA_ABILITY_BEHAVIOR_UNIT_TARGET 13 | end 14 | end 15 | ---@override 16 | function sniper_assassinate_lua:GetAbilityDamageType() 17 | if self:GetCaster():HasScepter() then 18 | return DAMAGE_TYPE_PHYSICAL 19 | else -- Normal (non-scepter) 20 | return DAMAGE_TYPE_MAGICAL 21 | end 22 | end 23 | ---@override 24 | function sniper_assassinate_lua:GetAOERadius() 25 | if self:GetCaster():HasScepter() then 26 | return self:GetSpecialValueFor("scepter_radius") 27 | else -- Normal (non-scepter) 28 | return 0 29 | end 30 | end 31 | 32 | ---@override 33 | function sniper_assassinate_lua:GetCastPoint() 34 | local time = self.BaseClass.GetCastPoint(self) 35 | local talent = self:GetCaster():FindAbilityByName("special_bonus_unique_sniper_4") 36 | if talent then 37 | time = time - talent:GetSpecialValueFor("value") 38 | end 39 | return time 40 | end 41 | 42 | ---@override 43 | function sniper_assassinate_lua:OnAbilityPhaseStart(keys) 44 | local caster = self:GetCaster() 45 | caster:EmitSound("Ability.AssassinateLoad") 46 | -- Store the target(s) in self.storedTarget, apply a modifier that reveals them 47 | self.storedTarget = {} 48 | if caster:HasScepter() then 49 | local point = self:GetCursorPosition() 50 | self.storedTarget = FindUnitsInRadius(caster:GetTeamNumber(),point,caster,self:GetSpecialValueFor("scepter_radius"), DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NO_INVIS, FIND_CLOSEST, false) 51 | for k,v in pairs(self.storedTarget) do 52 | v:AddNewModifier(caster,self,"modifier_sniper_assassinate_target_lua",{}) 53 | end 54 | else -- Normal (non-scepter) 55 | self.storedTarget[1] = self:GetCursorTarget() 56 | self.storedTarget[1]:AddNewModifier(caster,self,"modifier_sniper_assassinate_target_lua",{}) -- Make this 57 | 58 | end 59 | return true 60 | end 61 | ---@override 62 | function sniper_assassinate_lua:OnAbilityPhaseInterrupted() 63 | -- Remove the crosshairs from the target(s), and remove the modifier from the caster 64 | if self.storedTarget then 65 | for k,v in pairs(self.storedTarget) do 66 | v:RemoveModifierByName("modifier_sniper_assassinate_target_lua") 67 | end 68 | end 69 | self.storedTarget = nil 70 | self:GetCaster():RemoveModifierByNameAndCaster("modifier_sniper_assassinate_caster_lua",self:GetCaster()) 71 | end 72 | ---@override 73 | function sniper_assassinate_lua:OnSpellStart(keys) 74 | self:GetCaster():EmitSound("Ability.Assassinate") 75 | 76 | if not self.storedTarget then -- Should never happen, but to prevent errors we return here 77 | return 78 | end 79 | -- Because we stored the targets in a table, it is easy to fire a projectile at all of them 80 | for k,v in pairs(self.storedTarget) do 81 | local projTable = { 82 | EffectName = "particles/units/heroes/hero_sniper/sniper_assassinate.vpcf", 83 | Ability = self, 84 | Target = v, 85 | Source = self:GetCaster(), 86 | bDodgeable = true, 87 | bProvidesVision = true, 88 | vSpawnOrigin = self:GetCaster():GetAbsOrigin(), 89 | iMoveSpeed = self:GetSpecialValueFor("projectile_speed"), -- 90 | iVisionRadius = 100,-- 91 | iVisionTeamNumber = self:GetCaster():GetTeamNumber(), 92 | iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_ATTACK_1 93 | } 94 | ProjectileManager:CreateTrackingProjectile(projTable) 95 | end 96 | end 97 | ---@override 98 | function sniper_assassinate_lua:OnProjectileHit(hTarget,vLocation) 99 | local caster = self:GetCaster() 100 | local target = hTarget 101 | 102 | 103 | -- Linkens dodge 104 | if not self:GetCaster():HasScepter() then 105 | if target:TriggerSpellAbsorb(self) then 106 | return true 107 | end 108 | end 109 | 110 | target:EmitSound("Hero_Sniper.AssassinateDamage") 111 | 112 | if caster:HasScepter() then 113 | -- Quickly create and remove the crit modifier 114 | caster:AddNewModifier(caster,self,"modifier_sniper_assassinate_caster_lua",{}) 115 | caster:PerformAttack(target,true,true,true,true,false, false, true) 116 | caster:RemoveModifierByName("modifier_sniper_assassinate_caster_lua") 117 | else -- Normal (non-scepter) 118 | local damageTable = { 119 | victim = target, 120 | attacker = caster, 121 | damage = self:GetAbilityDamage(), 122 | damage_type = self:GetAbilityDamageType(), 123 | } 124 | ApplyDamage(damageTable) 125 | end 126 | -- Remove the crosshair+vision 127 | target:RemoveModifierByName("modifier_sniper_assassinate_target_lua") 128 | 129 | self.storedTarget[target] = nil 130 | for k,v in pairs(self.storedTarget) do 131 | if v == target then 132 | self.storedTarget[k] = nil 133 | end 134 | end 135 | return true 136 | end 137 | -- Marks the target(s) and provides vision for them 138 | ---@class modifier_sniper_assassinate_target_lua : CDOTA_Modifier_Lua 139 | modifier_sniper_assassinate_target_lua = class({}) 140 | ---@override 141 | function modifier_sniper_assassinate_target_lua:IsHidden() 142 | return true 143 | end 144 | ---@override 145 | function modifier_sniper_assassinate_target_lua:IsPurgable() 146 | return false 147 | end 148 | ---@override 149 | function modifier_sniper_assassinate_target_lua:IsDebuff() 150 | return true 151 | end 152 | ---@override 153 | function modifier_sniper_assassinate_target_lua:GetEffectName() 154 | return "particles/units/heroes/hero_sniper/sniper_crosshair.vpcf" 155 | end 156 | ---@override 157 | function modifier_sniper_assassinate_target_lua:GetEffectAttachType() 158 | return PATTACH_OVERHEAD_FOLLOW 159 | end 160 | ---@override 161 | function modifier_sniper_assassinate_target_lua:CheckStates() 162 | local state = { 163 | [MODIFIER_STATE_INVISIBLE] = false, 164 | } 165 | return state 166 | end 167 | ---@override 168 | function modifier_sniper_assassinate_target_lua:DeclareFunctions() 169 | local funcs = { 170 | MODIFIER_PROPERTY_PROVIDES_FOW_POSITION, 171 | } 172 | end 173 | ---@override 174 | function modifier_sniper_assassinate_target_lua:GetModifierProvidesFOWVision() 175 | return 1 176 | end 177 | ---@override 178 | function modifier_sniper_assassinate_target_lua:OnCreated() 179 | if IsServer() then 180 | self:StartIntervalThink(FrameTime()) 181 | end 182 | end 183 | -- This modifier provides the crit 184 | ---@class modifier_sniper_assassinate_caster_lua : CDOTA_Modifier_Lua 185 | modifier_sniper_assassinate_caster_lua = class({}) 186 | ---@override 187 | function modifier_sniper_assassinate_caster_lua:IsHidden() 188 | return true 189 | end 190 | ---@override 191 | function modifier_sniper_assassinate_caster_lua:IsPurgable() 192 | return false 193 | end 194 | ---@override 195 | function modifier_sniper_assassinate_caster_lua:DeclareFunctions() 196 | local funcs = { 197 | MODIFIER_PROPERTY_PREATTACK_CRITICALSTRIKE, 198 | } 199 | return funcs 200 | end 201 | ---@override 202 | function modifier_sniper_assassinate_caster_lua:GetModifierPreAttack_CriticalStrike() 203 | if IsServer() then 204 | return self:GetAbility():GetSpecialValueFor("scepter_crit_bonus") 205 | end 206 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/sniper/headshot.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_sniper_headshot_passive","heroes/sniper/headshot.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_sniper_headshot_enemies","heroes/sniper/headshot.lua",LUA_MODIFIER_MOTION_NONE) 3 | 4 | ---@class sniper_headshot_lua : CDOTA_Ability_Lua 5 | sniper_headshot_lua = class({}) 6 | ---@override 7 | function sniper_headshot_lua:GetIntrinsicModifierName() 8 | return "modifier_sniper_headshot_passive" 9 | end 10 | 11 | ---@class modifier_sniper_headshot_passive : CDOTA_Modifier_Lua 12 | modifier_sniper_headshot_passive = class({}) 13 | ---@override 14 | function modifier_sniper_headshot_passive:DeclareFunctions() 15 | return { 16 | --MODIFIER_EVENT_ON_ATTACK_START, 17 | MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_PHYSICAL, 18 | } 19 | end 20 | ---@override 21 | function modifier_sniper_headshot_passive:GetModifierProcAttack_BonusDamage_Physical(keys) -- This only triggers serversside on attack. 22 | if IsServer() then 23 | local target = keys.target 24 | local caster = self:GetCaster() 25 | local ability = self:GetAbility() 26 | if caster:PassivesDisabled() then return 0 end 27 | if target:IsBuilding() or target:IsOther() then return 0 end 28 | if RollPercentage(ability:GetSpecialValueFor("proc_chance")) then 29 | target:AddNewModifier(caster,self:GetAbility(),"modifier_sniper_headshot_enemies",{ duration = ability:GetSpecialValueFor("slow_duration")}) 30 | local talent = caster:FindAbilityByName("special_bonus_unique_sniper_3") 31 | if talent and talent:GetLevel() > 0 then 32 | local knockback_dist = talent:GetSpecialValueFor("value") 33 | local knockback = { 34 | should_stun = 0, 35 | knockback_duration = 0.1, -- Wiki says knockback speed is 350, 35/350 = 0.1 36 | duration = 0.1, 37 | knockback_distance = knockback_dist, 38 | knockback_height = 0, 39 | center_x = caster:GetAbsOrigin().x, 40 | center_y = caster:GetAbsOrigin().y, 41 | center_z = caster:GetAbsOrigin().z, 42 | } 43 | -- If using motion controllers, this has the lowest priority 44 | target:AddNewModifier(caster,self:GetAbility(),"modifier_knockback",knockback) 45 | end 46 | else 47 | return 0 48 | end 49 | end 50 | end 51 | 52 | ---@class modifier_sniper_headshot_enemies : CDOTA_Modifier_Lua 53 | modifier_sniper_headshot_enemies = class({}) 54 | 55 | ---@override 56 | function modifier_sniper_headshot_enemies:DeclareFunctions() 57 | return { 58 | MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 59 | MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, 60 | } 61 | end 62 | ---@override 63 | function modifier_sniper_headshot_enemies:GetModifierMoveSpeedBonus_Percentage() 64 | return self:GetAbility():GetSpecialValueFor("slow") 65 | end 66 | 67 | ---@override 68 | function modifier_sniper_headshot_enemies:GetModifierAttackSpeedBonus_Constant() 69 | return self:GetAbility():GetSpecialValueFor("slow") 70 | end 71 | 72 | ---@override 73 | function modifier_sniper_headshot_enemies:GetEffectName() 74 | return "particles/units/heroes/hero_sniper/sniper_headshot_slow.vpcf" 75 | end 76 | 77 | ---@override 78 | function modifier_sniper_headshot_enemies:GetEffectAttachType() 79 | return PATTACH_OVERHEAD_FOLLOW 80 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/sniper/shrapnel.lua: -------------------------------------------------------------------------------- 1 | -- IMPORTANT! 2 | -- Add the "base_ability_charges" from npc_abilities_custom.txt to your npc_abilities_custom.txt file for this to work! 3 | -- Also store the util/base_ability_charges.lua somewhere, and adjust the require path below. 4 | -- The modifier you link should be named "modifier_" YOUR_ABILITY_NAME "_charges", in this case modifier_sniper_shrapnel_lua_charges 5 | require('heroes/util/base_ability_charges') 6 | 7 | LinkLuaModifier("modifier_sniper_shrapnel_lua_charges","heroes/sniper/shrapnel.lua",LUA_MODIFIER_MOTION_NONE) 8 | LinkLuaModifier("modifier_sniper_shrapnel_lua_aura","heroes/sniper/shrapnel.lua",LUA_MODIFIER_MOTION_NONE) 9 | LinkLuaModifier("modifier_sniper_shrapnel_lua_aura_modifier","heroes/sniper/shrapnel.lua",LUA_MODIFIER_MOTION_NONE) 10 | 11 | -- Using the baseclass of the charges modifier instead of creating a new one 12 | ---@class sniper_shrapnel_lua : base_ability_charges 13 | sniper_shrapnel_lua = class(base_ability_charges) 14 | ---@override 15 | function sniper_shrapnel_lua:GetAOERadius() 16 | return self:GetSpecialValueFor("radius") 17 | end 18 | 19 | ---@override 20 | function sniper_shrapnel_lua:OnSpellStart() 21 | local caster = self:GetCaster() 22 | local point = self:GetCursorPosition() 23 | local duration = self:GetSpecialValueFor("duration") 24 | local radius = self:GetSpecialValueFor("radius") 25 | local talent = caster:FindAbilityByName("special_bonus_unique_sniper_2") 26 | if talent and talent:GetLevel() ~= 0 then 27 | self:GetIntrinsicModifierHandle().max_charges = self:GetSpecialValueFor("max_charges") + talent:GetSpecialValueFor("value") 28 | end 29 | -- Provide vision 30 | self:CreateVisibilityNode(point,radius,duration) 31 | 32 | local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_sniper/sniper_shrapnel_launch.vpcf", PATTACH_CUSTOMORIGIN_FOLLOW, caster) 33 | ParticleManager:SetParticleControlEnt(particle, 0, caster, PATTACH_POINT_FOLLOW, "attach_attack1", caster:GetAbsOrigin(), true) 34 | ParticleManager:SetParticleControl(particle, 1, point+Vector(0,0,1000)) 35 | ParticleManager:ReleaseParticleIndex(particle) 36 | 37 | caster:EmitSound("Hero_Sniper.ShrapnelShoot") 38 | 39 | --Delay the effect 40 | self:SetContextThink("shrapnel_delay",function() 41 | CreateModifierThinker(caster,self,"modifier_sniper_shrapnel_lua_aura",{duration = duration},point,caster:GetTeamNumber(),false) 42 | 43 | caster:EmitSound("Hero_Sniper.ShrapnelShatter" ) 44 | end, self:GetSpecialValueFor("damage_delay")) 45 | end 46 | 47 | ---@class modifier_sniper_shrapnel_lua_charges : modifier_base_ability_charges 48 | modifier_sniper_shrapnel_lua_charges = class(modifier_base_ability_charges) 49 | 50 | ---@class modifier_sniper_shrapnel_lua_aura : CDOTA_Modifier_Lua 51 | modifier_sniper_shrapnel_lua_aura = class({}) 52 | ---@override 53 | function modifier_sniper_shrapnel_lua_aura:OnCreated() 54 | if IsServer() then 55 | -- Ability specials 56 | self.radius = self:GetAbility():GetSpecialValueFor("radius") 57 | 58 | -- Add shrapnel particle effect 59 | self.particle = ParticleManager:CreateParticle("particles/units/heroes/hero_sniper/sniper_shrapnel.vpcf", PATTACH_WORLDORIGIN, nil) 60 | ParticleManager:SetParticleControl(self.particle, 0, self:GetParent():GetAbsOrigin()) 61 | ParticleManager:SetParticleControl(self.particle, 1, Vector(self.radius, self.radius, 0)) 62 | ParticleManager:SetParticleControl(self.particle, 2, self:GetParent():GetAbsOrigin()) 63 | self:AddParticle(self.particle, false, false, -1, false, false) 64 | end 65 | end 66 | ---@override 67 | function modifier_sniper_shrapnel_lua_aura:IsAura() return true end 68 | ---@override 69 | function modifier_sniper_shrapnel_lua_aura:GetAuraSearchTeam() 70 | return DOTA_UNIT_TARGET_TEAM_ENEMY 71 | end 72 | ---@override 73 | function modifier_sniper_shrapnel_lua_aura:GetAuraRadius() 74 | return self.radius 75 | end 76 | ---@override 77 | function modifier_sniper_shrapnel_lua_aura:GetModifierAura() 78 | return "modifier_sniper_shrapnel_lua_aura_modifier" 79 | end 80 | ---@override 81 | function modifier_sniper_shrapnel_lua_aura:GetAuraSearchType() 82 | return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC 83 | end 84 | function modifier_sniper_shrapnel_lua_aura:GetAuraDuration() 85 | return self:GetAbility():GetSpecialValueFor("slow_duration") 86 | end 87 | ---@class modifier_sniper_shrapnel_lua_aura_modifier : CDOTA_Modifier_Lua 88 | modifier_sniper_shrapnel_lua_aura_modifier = class({}) 89 | ---@override 90 | function modifier_sniper_shrapnel_lua_aura_modifier:OnCreated() 91 | if IsServer then 92 | self.damage = self:GetAbility():GetSpecialValueFor("shrapnel_damage") 93 | local talent = caster:FindAbilityByName("special_bonus_unique_sniper_1") 94 | if talent and talent:GetLevel() ~= 0 then 95 | self.damage = self.damage + talent:GetSpecialValueFor("value") 96 | end 97 | self:StartIntervalThink(1) 98 | self:OnIntervalThink() 99 | end 100 | end 101 | 102 | function modifier_sniper_shrapnel_lua_aura_modifier:OnIntervalThink() 103 | local damageTable = { 104 | victim = self:GetParent(), 105 | attacker = self:GetCaster(), 106 | damage = self.damage, 107 | damage_type = DAMAGE_TYPE_MAGICAL, 108 | } 109 | ApplyDamage(damageTable) 110 | end 111 | ---@override 112 | function modifier_sniper_shrapnel_lua_aura_modifier:DeclareFunctions() 113 | return { 114 | MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 115 | } 116 | end 117 | 118 | ---@override 119 | function modifier_sniper_shrapnel_lua_aura_modifier:GetModifierMoveSpeedBonus_Percentage() 120 | return self:GetAbility():GetSpecialValueFor("slow_movement_speed") 121 | end 122 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/sniper/take_aim.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_sniper_take_aim_lua","heroes/sniper/take_aim.lua",LUA_MODIFIER_MOTION_NONE) 2 | 3 | ---@class sniper_take_aim_lua : CDOTA_Ability_Lua 4 | sniper_take_aim_lua = class({}) 5 | ---@override 6 | function sniper_take_aim_lua:GetIntrinsicModifierName() 7 | return "modifier_sniper_take_aim_lua" 8 | end 9 | 10 | ---@class modifier_sniper_take_aim_lua : CDOTA_Modifier_Lua 11 | modifier_sniper_take_aim_lua = class({}) 12 | ---@override 13 | function modifier_sniper_take_aim_lua:DeclareFunctions() 14 | return { 15 | MODIFIER_PROPERTY_ATTACK_RANGE_BONUS, 16 | } 17 | end 18 | ---@override 19 | function modifier_sniper_take_aim_lua:GetModifierAttackRangeBonus() 20 | return self:GetAbility():GetSpecialValueFor("bonus_attack_range") 21 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/tusk/frozen_sigil.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_tusk_sigil_slow_aura","heroes/tusk/frozen_sigil.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_tusk_sigil_slow_aura_modifier","heroes/tusk/frozen_sigil.lua",LUA_MODIFIER_MOTION_NONE) 3 | 4 | ---@class tusk_frozen_sigil_lua : CDOTA_Ability_Lua 5 | tusk_frozen_sigil_lua = class({}) 6 | ---@override 7 | function tusk_frozen_sigil_lua:OnSpellStart() 8 | local caster = self:GetCaster() 9 | local sigil_duration = self:GetSpecialValueFor("sigil_duration") 10 | -- Unit can be found in npc_dota_units.txt file 11 | local unit = CreateUnitByName("npc_dota_tusk_frozen_sigil"..self:GetLevel(),caster:GetAbsOrigin(),false,caster,caster:GetPlayerOwner(),caster:GetTeamNumber()) 12 | unit:SetControllableByPlayer(caster:GetPlayerOwnerID(),false) 13 | --Transfer this ability to the sigil, so there can't be nil references when this is stolen 14 | local ability = unit:AddAbility(self:GetAbilityName()) 15 | ability:SetLevel(self:GetLevel()) 16 | ability:SetHidden(true) 17 | -- Default modifier, can't be recreated. 18 | unit:AddNewModifier(caster,ability,"modifier_kill",{duration = sigil_duration}) 19 | unit:AddNewModifier(caster,ability,"modifier_tusk_sigil_slow_aura",{duration = sigil_duration}) 20 | end 21 | 22 | ---@class modifier_tusk_sigil_slow_aura : CDOTA_Modifier_Lua 23 | modifier_tusk_sigil_slow_aura = class({}) 24 | ---@override 25 | function modifier_tusk_sigil_slow_aura:OnCreated() 26 | self.radius = self:GetAbility():GetSpecialValueFor("sigil_radius") 27 | if IsServer() then 28 | self:GetParent():EmitSound("Hero_Tusk.FrozenSigil") 29 | local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_tusk/tusk_frozen_sigil.vpcf", PATTACH_ABSORIGIN_FOLLOW, self:GetParent()) 30 | end 31 | end 32 | -- This also makes it permanent 33 | ---@override 34 | function modifier_tusk_sigil_slow_aura:IsAura() return true end 35 | ---@override 36 | function modifier_tusk_sigil_slow_aura:IsDebuff() return false end 37 | ---@override 38 | function modifier_tusk_sigil_slow_aura:GetAuraSearchTeam() 39 | return DOTA_UNIT_TARGET_TEAM_ENEMY 40 | end 41 | ---@override 42 | function modifier_tusk_sigil_slow_aura:GetAuraRadius() 43 | return self.radius 44 | end 45 | ---@override 46 | function modifier_tusk_sigil_slow_aura:GetModifierAura() 47 | return "modifier_tusk_sigil_slow_aura_modifier" 48 | end 49 | ---@override 50 | function modifier_tusk_sigil_slow_aura:GetAuraSearchType() 51 | return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC 52 | end 53 | ---@override 54 | function modifier_tusk_sigil_slow_aura:DeclareFunctions() 55 | return { 56 | MODIFIER_PROPERTY_TOTAL_CONSTANT_BLOCK 57 | } 58 | end 59 | ---@override 60 | function modifier_tusk_sigil_slow_aura:CheckState() 61 | return { 62 | [MODIFIER_STATE_MAGIC_IMMUNE] = true 63 | } 64 | end 65 | -- This does show a damage block icon, but I think it's better than setting health 66 | ---@override 67 | function modifier_tusk_sigil_slow_aura:GetModifierTotal_ConstantBlock(keys) 68 | local damage = keys.damage 69 | local attacker = keys.attacker 70 | -- Block all damage from abilities 71 | if keys.inflictor then return keys.damage end 72 | if attacker:IsRealHero() then 73 | -- Sigil takes 4 damage from heroes 74 | return keys.damage - 4 75 | else 76 | -- Sigil takes 1 damage from heroes 77 | return keys.damage - 1 78 | end 79 | end 80 | 81 | ---@override 82 | function modifier_tusk_sigil_slow_aura:GetEffectAttachType() 83 | return PATTACH_ABSORIGIN_FOLLOW 84 | end 85 | 86 | ---@class modifier_tusk_sigil_slow_aura_modifier : CDOTA_Modifier_Lua 87 | modifier_tusk_sigil_slow_aura_modifier = class({}) 88 | 89 | ---@override 90 | function modifier_tusk_sigil_slow_aura_modifier:DeclareFunctions() 91 | return { 92 | MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 93 | MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, 94 | } 95 | end 96 | ---@override 97 | function modifier_tusk_sigil_slow_aura_modifier:GetModifierAttackSpeedBonus_Constant() 98 | return -1 * self:GetAbility():GetSpecialValueFor("attack_slow") 99 | end 100 | 101 | ---@override 102 | function modifier_tusk_sigil_slow_aura_modifier:GetModifierMoveSpeedBonus_Percentage() 103 | return -1 * self:GetAbility():GetSpecialValueFor("move_slow") 104 | end 105 | 106 | ---@override 107 | function modifier_tusk_sigil_slow_aura_modifier :GetStatusEffectName() 108 | return "particles/units/heroes/hero_tusk/tusk_frozen_sigil_status.vpcf" 109 | end 110 | 111 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/tusk/ice_shards.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_tusk_ice_shards_dummy","heroes/tusk/ice_shards.lua",LUA_MODIFIER_MOTION_NONE) 2 | 3 | ---@class tusk_ice_shards_lua : CDOTA_Ability_Lua 4 | tusk_ice_shards_lua = class({}) 5 | 6 | --- This does not display the reduced cooldown on the client, if you really want this use modifier with stacks to sync this. 7 | ---@override 8 | function tusk_ice_shards_lua:GetCooldown(iLevel) 9 | if IsServer()then 10 | local talent = self:GetCaster():FindAbilityByName("special_bonus_unique_tusk_5_lua") 11 | local reduction 12 | if talent then 13 | reduction = talent:GetSpecialValueFor("value") 14 | end 15 | return self.BaseClass.GetCooldown(self, iLevel) - reduction 16 | end 17 | return self.BaseClass.GetCooldown(self, iLevel) 18 | end 19 | 20 | ---@override 21 | function tusk_ice_shards_lua:OnSpellStart() 22 | local caster = self:GetCaster() 23 | local point = self:GetCursorPosition() 24 | local length = (point-caster:GetAbsOrigin()):Length2D() - self:GetSpecialValueFor("shard_distance") 25 | local direction = (point-caster:GetAbsOrigin()):Normalized() 26 | direction.z = 0 27 | -- Store this to decide in which way the arc goes 28 | self.direction = direction 29 | -- Create a dummy to block creep spawns, a modifier thinker does NOT do this! 30 | self.dummy = CreateUnitByName("npc_dota_units_base",caster:GetAbsOrigin(),false,nil,nil,caster:GetTeamNumber()) 31 | self.dummy:AddNewModifier(caster,self,"modifier_tusk_ice_shards_dummy",{}) 32 | local projectile_table = { 33 | Ability = self, 34 | EffectName = "particles/units/heroes/hero_tusk/tusk_ice_shards_projectile.vpcf", 35 | vSpawnOrigin = caster:GetAbsOrigin(), 36 | fDistance = length, 37 | fStartRadius = self:GetSpecialValueFor("shard_width"), 38 | fEndRadius = self:GetSpecialValueFor("shard_width"), 39 | Source = caster, 40 | bHasFrontalCone = false, 41 | bReplaceExisting = false, 42 | iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY, 43 | iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE, 44 | iUnitTargetType = DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC, 45 | fExpireTime = GameRules:GetGameTime() + 3, 46 | bDeleteOnHit = false, 47 | vVelocity = direction * self:GetSpecialValueFor("shard_speed"), 48 | bProvidesVision = true, 49 | iVisionRadius = self:GetSpecialValueFor("shard_width"), 50 | iVisionTeamNumber = caster:GetTeamNumber() 51 | } 52 | ProjectileManager:CreateLinearProjectile(projectile_table) 53 | 54 | caster:EmitSound("Hero_Tusk.IceShards.Projectile") 55 | end 56 | ---@override 57 | function tusk_ice_shards_lua:OnProjectileThink(vLocation) 58 | self.dummy:SetAbsOrigin(vLocation) 59 | end 60 | ---@override 61 | function tusk_ice_shards_lua:OnProjectileHit(hTarget, vLocation) 62 | if hTarget then 63 | local damage_table = { 64 | victim = hTarget, 65 | attacker = self:GetCaster(), 66 | ability = self, 67 | damage = self:GetSpecialValueFor("shard_damage"), 68 | damage_type = self:GetAbilityDamageType(), 69 | } 70 | ApplyDamage(damage_table ) 71 | else 72 | self:GetCaster():StopSound("Hero_Tusk.IceShards.Projectile") 73 | self:GetCaster():EmitSound("Hero_Tusk.IceShards") 74 | UTIL_Remove(self.dummy) 75 | local shard_distance = self:GetSpecialValueFor("shard_distance") 76 | local shard_angle_step = self:GetSpecialValueFor("shard_angle_step") 77 | self.shards = {} 78 | self.blockers = {} 79 | -- 7 shards, from -120 to 120 80 | local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_tusk/tusk_ice_shards.vpcf",PATTACH_WORLDORIGIN,self:GetCaster()) 81 | ParticleManager:SetParticleControl(particle,0,Vector(self:GetSpecialValueFor("shard_duration"),0,0)) 82 | for i=0,6 do 83 | local angle = -120 + i * shard_angle_step 84 | local direction = RotatePosition(Vector(0,0,0), QAngle(0,angle,0), self.direction) 85 | local position = GetGroundPosition(vLocation + direction * shard_distance,nil) 86 | self.blockers[i] = SpawnEntityFromTableSynchronous("point_simple_obstruction", {origin = position}) 87 | -- Using a particle for this is easier and looks better 88 | --[[self.shards[i] = SpawnEntityFromTableSynchronous("prop_dynamic", {model = "models/particle/ice_shards.vmdl", DefaultAnim=animation, targetname=DoUniqueString("prop_dynamic")}) 89 | self.shards[i]:SetAbsOrigin(position) 90 | self.shards[i]:SetForwardVector(vLocation-position) 91 | self.shards[i]:SetModelScale(15)]] 92 | ParticleManager:SetParticleControl(particle,i+1,position) 93 | end 94 | 95 | self:SetContextThink("think_duration",function() self:RemoveShards() end,self:GetSpecialValueFor("shard_duration")) 96 | end 97 | end 98 | 99 | function tusk_ice_shards_lua:RemoveShards() 100 | for i=0,6 do 101 | UTIL_Remove(self.blockers[i]) 102 | end 103 | self.blockers = nil 104 | end 105 | 106 | ---@class modifier_tusk_ice_shards_dummy : CDOTA_Modifier_Lua 107 | modifier_tusk_ice_shards_dummy = class({}) 108 | ---@override 109 | function modifier_tusk_ice_shards_dummy:IsPermanent() return true end 110 | ---@return table 111 | function modifier_tusk_ice_shards_dummy:CheckState() 112 | return { 113 | [MODIFIER_STATE_NO_HEALTH_BAR] = true, 114 | [MODIFIER_STATE_NO_UNIT_COLLISION] = true, 115 | [MODIFIER_STATE_INVULNERABLE] = true, 116 | } 117 | end 118 | 119 | 120 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/tusk/walrus_kick.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_tusk_walrus_kick_flying","heroes/tusk/walrus_kick.lua",LUA_MODIFIER_MOTION_NONE) 2 | LinkLuaModifier("modifier_tusk_walrus_kick_slow","heroes/tusk/walrus_kick.lua",LUA_MODIFIER_MOTION_NONE) 3 | 4 | ---@class tusk_walrus_kick_lua : CDOTA_Ability_Lua 5 | tusk_walrus_kick_lua = class({}) 6 | 7 | ---@override 8 | function tusk_walrus_kick_lua:OnInventoryContentsChanged() 9 | self:SetHidden(not self:GetCaster():HasScepter()) 10 | -- Find the scepter and make it undroppable 11 | for i=DOTA_ITEM_SLOT_1,DOTA_ITEM_SLOT_6 do 12 | local item = self:GetCaster():GetItemInSlot(i) 13 | if item then 14 | item:SetDroppable(false) 15 | item:SetSellable(false) 16 | item:SetCanBeUsedOutOfInventory(false ) -- Would this prevent it from going in backpack? 17 | end 18 | end 19 | 20 | self:SetLevel(1) 21 | end 22 | ---@override 23 | function tusk_walrus_kick_lua:OnSpellStart() 24 | local caster = self:GetCaster() 25 | local target = self:GetCursorTarget() 26 | local direction = caster:GetForwardVector() 27 | local air_time_duration = self:GetSpecialValueFor("air_time") 28 | local duration = self:GetSpecialValueFor("slow_duration") 29 | local damage = self:GetSpecialValueFor("damage") 30 | local damage_type = self:GetAbilityDamageType() 31 | 32 | local damage_table = { 33 | ability = self, 34 | attacker = caster, 35 | victim = target, 36 | damage = damage, 37 | damage_type = damage_type, 38 | } 39 | 40 | ApplyDamage(damage_table) 41 | 42 | target:AddNewModifier(caster,self,"modifier_tusk_walrus_kick_flying",{duration = air_time_duration,dir_x = direction.x,dir_y=direction.y}) 43 | target:AddNewModifier(caster,self,"modifier_tusk_walrus_kick_slow",{duration = duration}) 44 | 45 | -- Text particles 46 | local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_tusk/tusk_walruskick_txt_ult.vpcf", PATTACH_ABSORIGIN, caster) 47 | ParticleManager:SetParticleControl(particle, 2, caster:GetAbsOrigin()+Vector(0,0,175)) 48 | ParticleManager:ReleaseParticleIndex(particle) 49 | caster:EmitSound("Hero_Tusk.WalrusKick.Target") 50 | end 51 | 52 | -- This modifier does not yet use a motion controller! It should start using some system to prevent conflicts 53 | ---@class modifier_tusk_walrus_kick_flying : CDOTA_Modifier_Lua 54 | modifier_tusk_walrus_kick_flying = class({}) 55 | 56 | ---@override 57 | function modifier_tusk_walrus_kick_flying:CheckState() 58 | return { 59 | [MODIFIER_STATE_STUNNED] = IsServer(), -- Not showing the status bar 60 | } 61 | end 62 | 63 | ---@override 64 | function modifier_tusk_walrus_kick_flying:DeclareFunctions() 65 | return { 66 | MODIFIER_PROPERTY_OVERRIDE_ANIMATION, 67 | } 68 | end 69 | 70 | ---@override 71 | function modifier_tusk_walrus_kick_flying:GetOverrideAnimation() 72 | return ACT_DOTA_FLAIL 73 | end 74 | 75 | ---@override 76 | function modifier_tusk_walrus_kick_flying:OnCreated(keys) 77 | if IsServer() then 78 | self.push_length = self:GetAbility():GetSpecialValueFor("push_length") 79 | self.air_time_duration = self:GetAbility():GetSpecialValueFor("air_time") 80 | 81 | self.push_speed = self.push_length/self.air_time_duration 82 | local max_height = 360 83 | -- The height that needs to be gained when the unit moves 1 unit. 84 | self.z_vel = (max_height/self.air_time_duration) *4 85 | self.direction = Vector(keys.dir_x*self.push_speed,keys.dir_y*self.push_speed,self.z_vel) 86 | 87 | self:StartIntervalThink(FrameTime()) 88 | end 89 | end 90 | 91 | ---@override 92 | function modifier_tusk_walrus_kick_flying:OnIntervalThink() 93 | local unit = self:GetParent() 94 | -- Decrease the z velocity 95 | self.direction.z = self.direction.z - (self.z_vel *2 *FrameTime()) 96 | unit:SetAbsOrigin(unit:GetAbsOrigin() + self.direction * FrameTime()) 97 | end 98 | 99 | ---@override 100 | function modifier_tusk_walrus_kick_flying:OnDestroy() 101 | if IsServer() then 102 | -- Make sure the unit ends on the ground 103 | -- Don't think this is needed though, the engine sets unit to ground level on movement 104 | FindClearSpaceForUnit(self:GetParent(),self:GetParent():GetAbsOrigin(),true) 105 | end 106 | end 107 | 108 | 109 | ---@class modifier_tusk_walrus_kick_slow : CDOTA_Modifier_Lua 110 | modifier_tusk_walrus_kick_slow = class({}) 111 | 112 | ---@override 113 | function modifier_tusk_walrus_kick_slow:OnCreated() 114 | self.slow = self:GetAbility():GetSpecialValueFor("move_slow") 115 | end 116 | 117 | ---@override 118 | function modifier_tusk_walrus_kick_slow:DeclareFunctions() 119 | return { 120 | MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 121 | } 122 | end 123 | 124 | ---@override 125 | function modifier_tusk_walrus_kick_slow:GetModifierMoveSpeedBonus_Percentage() 126 | return self.slow 127 | end 128 | 129 | -- Particle effect 130 | ---@override 131 | function modifier_tusk_walrus_kick_slow:GetStatusEffectName() 132 | return "particles/units/heroes/hero_tusk/tusk_walruspunch_status.vpcf" 133 | end 134 | 135 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/tusk/walrus_punch.lua: -------------------------------------------------------------------------------- 1 | -- modifier_tusk_walrus_punch is taken by the game 2 | LinkLuaModifier("modifier_tusk_walrus_punch_lua","heroes/tusk/walrus_punch.lua",LUA_MODIFIER_MOTION_NONE) 3 | LinkLuaModifier("modifier_tusk_walrus_punch_crit","heroes/tusk/walrus_punch.lua",LUA_MODIFIER_MOTION_NONE) 4 | LinkLuaModifier("modifier_tusk_walrus_punch_slow","heroes/tusk/walrus_punch.lua",LUA_MODIFIER_MOTION_NONE) 5 | LinkLuaModifier("modifier_tusk_walrus_punch_flying","heroes/tusk/walrus_punch.lua",LUA_MODIFIER_MOTION_NONE) 6 | 7 | ---@class tusk_walrus_punch_lua : CDOTA_Ability_Lua 8 | tusk_walrus_punch_lua = class({}) 9 | 10 | ---@override 11 | function tusk_walrus_punch_lua:GetIntrinsicModifierName() 12 | return "modifier_tusk_walrus_punch_lua" 13 | end 14 | 15 | ---@param hTarget CDOTA_BaseNPC 16 | function tusk_walrus_punch_lua:CastWalrusPunch(hTarget) 17 | local caster = self:GetCaster() 18 | local target = hTarget 19 | 20 | local air_time_duration = self:GetSpecialValueFor("air_time") 21 | local duration = self:GetSpecialValueFor("slow_duration") 22 | 23 | caster:AddNewModifier(caster,self,"modifier_tusk_walrus_punch_crit",{}) 24 | -- Could also be in OnAttackLanded 25 | target:AddNewModifier(caster,self,"modifier_tusk_walrus_punch_flying",{duration = air_time_duration}) 26 | target:AddNewModifier(caster,self,"modifier_tusk_walrus_punch_slow",{duration = duration}) 27 | 28 | -- Text particles 29 | local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_tusk/tusk_walruspunch_txt_ult.vpcf", PATTACH_ABSORIGIN, caster) 30 | ParticleManager:SetParticleControl(particle, 2, caster:GetAbsOrigin()+Vector(0,0,175)) 31 | ParticleManager:ReleaseParticleIndex(particle) 32 | 33 | caster:EmitSound("Hero_Tusk.WalrusPunch.Cast") 34 | end 35 | 36 | ---@class modifier_tusk_walrus_punch_lua : CDOTA_Modifier_Lua 37 | modifier_tusk_walrus_punch_lua = class({}) 38 | 39 | ---@override 40 | function modifier_tusk_walrus_punch_lua:IsHidden() return false end 41 | ---@override 42 | function modifier_tusk_walrus_punch_lua:IsPermanent() return true end 43 | 44 | ---@override 45 | function modifier_tusk_walrus_punch_lua:DeclareFunctions() 46 | return { 47 | MODIFIER_EVENT_ON_ATTACK_START, 48 | } 49 | end 50 | 51 | ---@param hTarget CDOTA_BaseNPC 52 | ---@return boolean 53 | function modifier_tusk_walrus_punch_lua:IsValidToTrigger(hTarget) 54 | -- Rejection based on target 55 | if hTarget:GetTeamNumber() == self:GetCaster():GetTeamNumber() then return false end 56 | if hTarget:IsBuilding() or hTarget:IsOther() then return false end 57 | -- Talent cast doesn't require resources 58 | local talent = self:GetCaster():FindAbilityByName("special_bonus_unique_tusk_3_lua") 59 | if talent then 60 | local walrus_punch_chance = talent:GetSpecialValueFor("value") 61 | if RollPercentage(walrus_punch_chance) then 62 | return true 63 | end 64 | end 65 | 66 | if not self:GetAbility():IsCooldownReady() or not self:GetAbility():GetAutoCastState() then return false end 67 | if self:GetCaster():GetMana() < self:GetAbility():GetManaCost(-1) then return false end 68 | self:GetAbility():UseResources(true,false,true) 69 | return true 70 | end 71 | 72 | function modifier_tusk_walrus_punch_lua:OnAttackStart(keys) 73 | local target = keys.target 74 | local caster = self:GetCaster() 75 | local ability = self:GetAbility() 76 | 77 | if keys.attacker ~= self:GetCaster() then return end 78 | if not self:IsValidToTrigger(target) then return end 79 | -- I prefer to use the ability for this 80 | ability:CastWalrusPunch(target) 81 | end 82 | 83 | ---@class modifier_tusk_walrus_punch_crit : CDOTA_Modifier_Lua 84 | modifier_tusk_walrus_punch_crit = class({}) 85 | 86 | ---@override 87 | function modifier_tusk_walrus_punch_crit:DeclareFunctions() 88 | return { 89 | MODIFIER_PROPERTY_PREATTACK_CRITICALSTRIKE, 90 | MODIFIER_EVENT_ON_ATTACK_LANDED, 91 | } 92 | end 93 | 94 | ---@override 95 | function modifier_tusk_walrus_punch_crit:OnCreated() 96 | if IsServer() then 97 | local ability = self:GetAbility() 98 | self.crit_multiplier = ability:GetSpecialValueFor("crit_multiplier") 99 | local talent = self:GetCaster():FindAbilityByName("special_bonus_unique_tusk_3_lua") 100 | if talent then 101 | self.crit_multiplier = self.crit_multiplier + talent:GetSpecialValueFor("value") 102 | end 103 | end 104 | end 105 | 106 | 107 | ---@override 108 | function modifier_tusk_walrus_punch_crit:GetModifierPreAttack_CriticalStrike(keys) 109 | if IsServer() then -- Property won't be displayed on client anyway 110 | return self.crit_multiplier 111 | end 112 | end 113 | 114 | ---@override 115 | function modifier_tusk_walrus_punch_crit:OnAttackLanded() 116 | self:GetCaster():EmitSound("Hero_Tusk.WalrusPunch.Target") 117 | self:Destroy() 118 | end 119 | ---@class modifier_tusk_walrus_punch_flying : CDOTA_Modifier_Lua 120 | modifier_tusk_walrus_punch_flying = class({}) 121 | 122 | ---@override 123 | function modifier_tusk_walrus_punch_flying:CheckState() 124 | return { 125 | [MODIFIER_STATE_STUNNED] = IsServer(), -- Not showing the status bar 126 | } 127 | end 128 | 129 | ---@override 130 | function modifier_tusk_walrus_punch_flying:DeclareFunctions() 131 | return { 132 | MODIFIER_PROPERTY_OVERRIDE_ANIMATION, 133 | } 134 | end 135 | 136 | ---@override 137 | function modifier_tusk_walrus_punch_flying:GetOverrideAnimation() 138 | return ACT_DOTA_FLAIL 139 | end 140 | 141 | ---@override 142 | function modifier_tusk_walrus_punch_flying:OnCreated(keys) 143 | if IsServer() then 144 | self.air_time_duration = self:GetAbility():GetSpecialValueFor("air_time") 145 | local max_height = 650 146 | -- The height that needs to be gained when the unit moves 1 unit. 147 | self.z_vel = max_height * 4 148 | self.direction = Vector(0,0,self.z_vel) 149 | 150 | self:StartIntervalThink(FrameTime()) 151 | end 152 | end 153 | 154 | ---@override 155 | function modifier_tusk_walrus_punch_flying:OnIntervalThink() 156 | local unit = self:GetParent() 157 | -- Decrease the z velocity 158 | self.direction.z = self.direction.z - (self.z_vel *2 *FrameTime()) 159 | unit:SetAbsOrigin(unit:GetAbsOrigin() + self.direction * FrameTime()) 160 | end 161 | 162 | ---@override 163 | function modifier_tusk_walrus_punch_flying:OnDestroy() 164 | if IsServer() then 165 | -- Make sure the unit ends on the ground 166 | -- Don't think this is needed though, the engine sets unit to ground level on movement 167 | FindClearSpaceForUnit(self:GetParent(),self:GetParent():GetAbsOrigin(),true) 168 | end 169 | end 170 | 171 | 172 | ---@class modifier_tusk_walrus_punch_slow : CDOTA_Modifier_Lua 173 | modifier_tusk_walrus_punch_slow = class({}) 174 | 175 | ---@override 176 | function modifier_tusk_walrus_punch_slow:OnCreated() 177 | self.slow = self:GetAbility():GetSpecialValueFor("move_slow") 178 | end 179 | 180 | ---@override 181 | function modifier_tusk_walrus_punch_slow:DeclareFunctions() 182 | return { 183 | MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, 184 | } 185 | end 186 | 187 | ---@override 188 | function modifier_tusk_walrus_punch_slow:GetModifierMoveSpeedBonus_Percentage() 189 | return self.slow 190 | end 191 | 192 | ---@override 193 | function modifier_tusk_walrus_punch_slow:GetStatusEffectName() 194 | return "particles/units/heroes/hero_tusk/tusk_walruspunch_status.vpcf" 195 | end 196 | 197 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/util/base_ability_charges.lua: -------------------------------------------------------------------------------- 1 | LinkLuaModifier("modifier_base_ability_charges","heroes/sniper/assassinate.lua",LUA_MODIFIER_MOTION_NONE) 2 | 3 | ---@class base_ability_charges : CDOTA_Ability_Lua 4 | base_ability_charges = class({}) 5 | 6 | ---@overide 7 | function base_ability_charges:GetIntrinsicModifierName() 8 | return "modifier_"..self:GetAbilityName().."_charges" 9 | end 10 | ---@return CDOTA_Modifier_Lua 11 | function base_ability_charges:GetIntrinsicModifierHandle() 12 | return self:GetCaster():FindModifierByName(self:GetIntrinsicModifierName()) 13 | end 14 | ---Override this when you have another name in your abilityspecial values 15 | ---@return number 16 | function base_ability_charges:GetMaxChargeCount() 17 | return self:GetSpecialValueFor("max_charges") 18 | end 19 | ---Override this when you have another name in your abilityspecial values 20 | ---@return number 21 | function base_ability_charges:GetChargeRestoreTime() 22 | return self:GetSpecialValueFor("charge_restore_time") 23 | end 24 | ---Override this if you don't want this to start at max 25 | ---@return nil 26 | function base_ability_charges:GetStartStackCount() 27 | return nil 28 | end 29 | ---@override 30 | function base_ability_charges:GetCooldown() 31 | if IsServer() then 32 | return 0 33 | else 34 | return self:GetChargeRestoreTime() 35 | end 36 | end 37 | 38 | ---@class modifier_base_ability_charges : CDOTA_Modifier_Lua 39 | modifier_base_ability_charges = class({}) 40 | 41 | ---@override 42 | function modifier_base_ability_charges:IsDebuff() 43 | return false 44 | end 45 | ---@override 46 | function modifier_base_ability_charges:IsPermanent() 47 | return true 48 | end 49 | ---@override 50 | function modifier_base_ability_charges:OnCreated() 51 | if IsServer() then 52 | self.max_charges = self:GetAbility():GetMaxChargeCount() 53 | if self:GetAbility():GetStartStackCount() then 54 | self:SetStackCount(self:GetAbility():GetStartStackCount()) 55 | else 56 | self:SetStackCount(self.max_charges) 57 | end 58 | self.charge_restore_time = self:GetAbility():GetChargeRestoreTime() 59 | self:StartIntervalThink(FrameTime()) 60 | end 61 | end 62 | ---@override 63 | function modifier_base_ability_charges:OnRefresh() 64 | if IsServer() then 65 | self.max_charges = self:GetAbility():GetMaxChargeCount() 66 | self.charge_restore_time = self:GetAbility():GetChargeRestoreTime() 67 | end 68 | end 69 | ---@override 70 | function modifier_base_ability_charges:OnIntervalThink() 71 | if self:GetStackCount() == self.max_charges then 72 | self:SetDuration(self.charge_restore_time,true) 73 | return 74 | end 75 | if self:GetRemainingTime() <= 0 then 76 | self:SetStackCount(math.min(self.max_charges,self:GetStackCount()+1)) 77 | self:SetDuration(self.charge_restore_time,true) 78 | end 79 | end 80 | function modifier_base_ability_charges:DeclareFunctions() 81 | return { 82 | MODIFIER_EVENT_ON_ABILITY_FULLY_CAST, 83 | } 84 | end 85 | ---@override 86 | function modifier_base_ability_charges:OnAbilityFullyCast(keys) 87 | if IsServer() then 88 | if keys.unit == self:GetCaster() and keys.ability == self:GetAbility() then 89 | self:DecrementStackCount() 90 | if self:GetStackCount() == 0 then 91 | self:GetAbility():StartCooldown(self.charge_restore_time) 92 | end 93 | if self:GetRemainingTime() < 0 then 94 | self:SetDuration(self.charge_restore_time,true) 95 | end 96 | end 97 | end 98 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/vengefulspirit/command_aura.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ModDota AbilityLuaSpellLibrary implementation for Vengenge Aura 3 | 4 | Mechanics correct as at 7.07c (excluding talents) 5 | --]] 6 | 7 | LinkLuaModifier("modifier_vengefulspirit_command_aura_effect_lua", "heroes/vengefulspirit/command_aura", LUA_MODIFIER_MOTION_NONE) 8 | LinkLuaModifier("modifier_vengefulspirit_command_aura_lua", "heroes/vengefulspirit/command_aura", LUA_MODIFIER_MOTION_NONE) 9 | 10 | --[[ 11 | ============================== 12 | ===== Command Aura Effect ==== 13 | ============================== 14 | --]] 15 | ---@class modifier_vengefulspirit_command_aura_effect_lua : CDOTA_Modifier_Lua 16 | ---@field bonus_damage_pct number 17 | modifier_vengefulspirit_command_aura_effect_lua = class({}) 18 | 19 | ---@override 20 | function modifier_vengefulspirit_command_aura_effect_lua:IsDebuff() 21 | if self:GetCaster():GetTeamNumber() ~= self:GetParent():GetTeamNumber() then 22 | return true 23 | end 24 | 25 | return false 26 | end 27 | 28 | ---@override 29 | function modifier_vengefulspirit_command_aura_effect_lua:OnCreated() 30 | self.bonus_damage_pct = self:GetAbility():GetSpecialValueFor("bonus_damage_pct") 31 | end 32 | 33 | ---@override 34 | function modifier_vengefulspirit_command_aura_effect_lua:OnRefresh() 35 | self.bonus_damage_pct = self:GetAbility():GetSpecialValueFor("bonus_damage_pct") 36 | end 37 | 38 | ---@override 39 | function modifier_vengefulspirit_command_aura_effect_lua:DeclareFunctions() 40 | local funcs = { 41 | MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE, 42 | } 43 | return funcs 44 | end 45 | 46 | ---@override 47 | function modifier_vengefulspirit_command_aura_effect_lua:GetModifierBaseDamageOutgoing_Percentage() 48 | if self:GetCaster():PassivesDisabled() then 49 | return 0 50 | end 51 | 52 | if self:GetCaster():GetTeamNumber() ~= self:GetParent():GetTeamNumber() then 53 | return -self.bonus_damage_pct 54 | end 55 | 56 | return self.bonus_damage_pct 57 | end 58 | 59 | --[[ 60 | ============================ 61 | ===== Command Aura Aura ==== 62 | ============================ 63 | --]] 64 | ---@class modifier_vengefulspirit_command_aura_lua : CDOTA_Modifier_Lua 65 | ---@field aura_radius number 66 | modifier_vengefulspirit_command_aura_lua = class({}) 67 | 68 | ---@override 69 | function modifier_vengefulspirit_command_aura_lua:IsHidden() 70 | return true 71 | end 72 | 73 | ---@override 74 | function modifier_vengefulspirit_command_aura_lua:IsAura() 75 | return true 76 | end 77 | 78 | ---@override 79 | function modifier_vengefulspirit_command_aura_lua:GetModifierAura() 80 | return "modifier_vengefulspirit_command_aura_effect_lua" 81 | end 82 | 83 | ---@override 84 | function modifier_vengefulspirit_command_aura_lua:GetAuraSearchTeam() 85 | return DOTA_UNIT_TARGET_TEAM_FRIENDLY 86 | end 87 | 88 | ---@override 89 | function modifier_vengefulspirit_command_aura_lua:GetAuraSearchType() 90 | return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP 91 | end 92 | 93 | ---@override 94 | function modifier_vengefulspirit_command_aura_lua:GetAuraSearchFlags() 95 | return DOTA_UNIT_TARGET_FLAG_INVULNERABLE 96 | end 97 | 98 | ---@override 99 | function modifier_vengefulspirit_command_aura_lua:GetAuraRadius() 100 | return self.aura_radius 101 | end 102 | 103 | ---@override 104 | function modifier_vengefulspirit_command_aura_lua:OnCreated() 105 | self.aura_radius = self:GetAbility():GetSpecialValueFor("aura_radius") 106 | if IsServer() and self:GetParent() ~= self:GetCaster() then 107 | self:StartIntervalThink(0.5) 108 | end 109 | end 110 | 111 | ---@override 112 | function modifier_vengefulspirit_command_aura_lua:OnRefresh() 113 | self.aura_radius = self:GetAbility():GetSpecialValueFor("aura_radius") 114 | end 115 | 116 | ---@override 117 | function modifier_vengefulspirit_command_aura_lua:DeclareFunctions() 118 | local funcs = { 119 | MODIFIER_EVENT_ON_DEATH 120 | } 121 | return funcs 122 | end 123 | 124 | --TODO: This should be type annotated externally 125 | ---@class OnDeathParams 126 | ---@field attacker CDOTA_BaseNPC 127 | ---@field unit CDOTA_BaseNPC 128 | 129 | ---@param params OnDeathParams 130 | ---@override 131 | function modifier_vengefulspirit_command_aura_lua:OnDeath(params) 132 | if IsServer() then 133 | if self:GetCaster() == nil then 134 | return 135 | end 136 | 137 | if self:GetCaster():PassivesDisabled() then 138 | return 139 | end 140 | 141 | if self:GetCaster() ~= self:GetParent() then 142 | return 143 | end 144 | 145 | local hAttacker = params.attacker 146 | local hVictim = params.unit 147 | 148 | if hVictim ~= nil and hAttacker ~= nil and hVictim == self:GetCaster() and hAttacker:GetTeamNumber() ~= hVictim:GetTeamNumber() then 149 | ---@type CDOTA_BaseNPC 150 | local hAuraHolder 151 | if hAttacker:IsHero() then 152 | hAuraHolder = hAttacker 153 | elseif hAttacker:GetOwnerEntity() ~= nil then 154 | ---@type CDOTA_BaseNPC 155 | local hAuraHolderTmp = hAttacker:GetOwnerEntity() 156 | if hAuraHolder:IsHero() then 157 | hAuraHolder = hAuraHolderTmp 158 | end 159 | end 160 | 161 | if hAuraHolder ~= nil then 162 | local modifierTable = { 163 | duration = -1 164 | } 165 | --TODO: Figure out why IntelliJ is convinced AddNewModifier does not exist 166 | hAuraHolder:AddNewModifier(self:GetCaster(), self:GetAbility(), "modifier_vengefulspirit_command_aura_lua", modifierTable) 167 | 168 | local nFXIndex = ParticleManager:CreateParticle("particles/units/heroes/hero_vengeful/vengeful_negative_aura.vpcf", PATTACH_ABSORIGIN_FOLLOW, hAuraHolder) 169 | ParticleManager:SetParticleControlEnt(nFXIndex, 1, hVictim, PATTACH_ABSORIGIN_FOLLOW, nil, hVictim:GetOrigin(), false) 170 | ParticleManager:ReleaseParticleIndex(nFXIndex ) 171 | end 172 | end 173 | end 174 | 175 | return 176 | end 177 | 178 | ---@override 179 | function modifier_vengefulspirit_command_aura_lua:OnIntervalThink() 180 | if self:GetCaster() ~= self:GetParent() and self:GetCaster():IsAlive() then 181 | self:Destroy() 182 | end 183 | end 184 | 185 | --[[ 186 | ============================= 187 | ===== Command Aura Spell ==== 188 | ============================= 189 | --]] 190 | ---@class vengefulspirit_command_aura_lua : CDOTA_Ability_Lua 191 | vengefulspirit_command_aura_lua = class({}) 192 | 193 | ---@override 194 | function vengefulspirit_command_aura_lua:GetIntrinsicModifierName() 195 | return "modifier_vengefulspirit_command_aura_lua" 196 | end 197 | -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/vengefulspirit/magic_missle.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ModDota AbilityLuaSpellLibrary implementation for Magic Missle 3 | 4 | Mechanics correct as at 7.07c (excluding talents) 5 | --]] 6 | 7 | LinkLuaModifier("modifier_vengefulspirit_magic_missile_lua", "heroes/vengefulspirit/magic_missle", LUA_MODIFIER_MOTION_NONE) 8 | 9 | --[[ 10 | ============================== 11 | ===== Magic Missle Effect ==== 12 | ============================== 13 | --]] 14 | ---@class modifier_vengefulspirit_magic_missile_lua : CDOTA_Modifier_Lua 15 | modifier_vengefulspirit_magic_missile_lua = class({}) 16 | 17 | ---@override 18 | function modifier_vengefulspirit_magic_missile_lua:IsDebuff() 19 | return true 20 | end 21 | 22 | ---@override 23 | function modifier_vengefulspirit_magic_missile_lua:IsStunDebuff() 24 | return true 25 | end 26 | 27 | ---@override 28 | function modifier_vengefulspirit_magic_missile_lua:GetEffectName() 29 | return "particles/generic_gameplay/generic_stunned.vpcf" 30 | end 31 | 32 | ---@override 33 | function modifier_vengefulspirit_magic_missile_lua:GetEffectAttachType() 34 | return PATTACH_OVERHEAD_FOLLOW 35 | end 36 | 37 | ---@override 38 | function modifier_vengefulspirit_magic_missile_lua:DeclareFunctions() 39 | local funcs = { 40 | MODIFIER_PROPERTY_OVERRIDE_ANIMATION, 41 | } 42 | 43 | return funcs 44 | end 45 | 46 | ---@override 47 | function modifier_vengefulspirit_magic_missile_lua:GetOverrideAnimation() 48 | return ACT_DOTA_DISABLED 49 | end 50 | 51 | ---@override 52 | function modifier_vengefulspirit_magic_missile_lua:CheckState() 53 | local state = { 54 | [MODIFIER_STATE_STUNNED] = true, 55 | } 56 | 57 | return state 58 | end 59 | 60 | --[[ 61 | ============================= 62 | ===== Magic Missle Spell ==== 63 | ============================= 64 | --]] 65 | ---@class vengefulspirit_magic_missile_lua : CDOTA_Ability_Lua 66 | vengefulspirit_magic_missile_lua = class({}) 67 | 68 | 69 | ---@override 70 | function vengefulspirit_magic_missile_lua:OnSpellStart() 71 | local info = { 72 | EffectName = "particles/units/heroes/hero_vengeful/vengeful_magic_missle.vpcf", 73 | Ability = self, 74 | iMoveSpeed = self:GetSpecialValueFor("magic_missile_speed"), 75 | Source = self:GetCaster(), 76 | Target = self:GetCursorTarget(), 77 | iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_ATTACK_2 78 | } 79 | 80 | ProjectileManager:CreateTrackingProjectile(info) 81 | EmitSoundOn("Hero_VengefulSpirit.MagicMissile", self:GetCaster()) 82 | end 83 | 84 | ---@override 85 | function vengefulspirit_magic_missile_lua:OnProjectileHit(hTarget, vLocation) 86 | if hTarget ~= nil and (not hTarget:IsInvulnerable()) and (not hTarget:TriggerSpellAbsorb(self)) and (not hTarget:IsMagicImmune()) then 87 | EmitSoundOn("Hero_VengefulSpirit.MagicMissileImpact", hTarget) 88 | local magic_missile_stun = self:GetSpecialValueFor("magic_missile_stun") 89 | local magic_missile_damage = self:GetSpecialValueFor("magic_missile_damage") 90 | 91 | local damage = { 92 | victim = hTarget, 93 | attacker = self:GetCaster(), 94 | damage = magic_missile_damage, 95 | damage_type = DAMAGE_TYPE_MAGICAL, 96 | ability = self 97 | } 98 | 99 | ApplyDamage(damage) --TODO: Type annotate ApplyDamage to take table not handle 100 | --TODO: Figure out why IntelliJ is convinced AddNewModifier does not exist 101 | hTarget:AddNewModifier(self:GetCaster(), self, "modifier_vengefulspirit_magic_missile_lua", { duration = magic_missile_stun }) 102 | end 103 | 104 | return true 105 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/vengefulspirit/nether_swap.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ModDota AbilityLuaSpellLibrary implementation for Nether Swap 3 | 4 | Mechanics correct as at 7.07c 5 | --]] 6 | 7 | ---@class vengeful_nether_swap_lua : CDOTA_Ability_Lua 8 | vengefulspirit_nether_swap_lua = class({}) 9 | 10 | 11 | --TODO: Figure out how to type annotate this 12 | ---@override 13 | ---@return number 14 | function vengefulspirit_nether_swap_lua:GetAOERadius() 15 | return self:GetSpecialValueFor("radius") 16 | end 17 | 18 | ---@override 19 | function vengefulspirit_nether_swap_lua:CastFilterResultTarget(hTarget) 20 | if self:GetCaster() == hTarget then 21 | return UF_FAIL_CUSTOM 22 | end 23 | 24 | if (hTarget:IsCreep() and (not self:GetCaster():HasScepter())) or hTarget:IsAncient() then 25 | return UF_FAIL_CUSTOM 26 | end 27 | 28 | --TODO: Type Annotate UnitFilter 29 | local nResult = UnitFilter(hTarget, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP, DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, self:GetCaster():GetTeamNumber()) 30 | if nResult ~= UF_SUCCESS then 31 | return nResult 32 | end 33 | 34 | return UF_SUCCESS 35 | end 36 | 37 | ---@override 38 | function vengefulspirit_nether_swap_lua:GetCustomCastErrorTarget(hTarget) 39 | if self:GetCaster() == hTarget then 40 | return "#dota_hud_error_cant_cast_on_self" 41 | end 42 | 43 | if hTarget:IsAncient() then 44 | return "#dota_hud_error_cant_cast_on_ancient" 45 | end 46 | 47 | if hTarget:IsCreep() and (not self:GetCaster():HasScepter()) then 48 | return "#dota_hud_error_cant_cast_on_creep" 49 | end 50 | 51 | return "" 52 | end 53 | 54 | ---@override 55 | function vengefulspirit_nether_swap_lua:GetCooldown(nLevel) 56 | if self:GetCaster():HasScepter() then 57 | return self:GetSpecialValueFor("nether_swap_cooldown_scepter") 58 | end 59 | 60 | return self.BaseClass.GetCooldown(self, nLevel) 61 | end 62 | 63 | ---@override 64 | function vengefulspirit_nether_swap_lua:OnSpellStart() 65 | local hCaster = self:GetCaster() 66 | local hTarget = self:GetCursorTarget() 67 | 68 | if hCaster == nil or hTarget == nil or hTarget:TriggerSpellAbsorb(self) then 69 | return nil 70 | end 71 | 72 | local vPos1 = hCaster:GetOrigin() 73 | local vPos2 = hTarget:GetOrigin() 74 | 75 | GridNav:DestroyTreesAroundPoint(vPos1, 300, false) 76 | GridNav:DestroyTreesAroundPoint(vPos2, 300, false) 77 | 78 | hCaster:SetOrigin(vPos2) 79 | hTarget:SetOrigin(vPos1) 80 | 81 | FindClearSpaceForUnit(hCaster, vPos2, true) 82 | FindClearSpaceForUnit(hTarget, vPos1, true) 83 | 84 | hTarget:Interrupt() 85 | 86 | local nCasterFX = ParticleManager:CreateParticle("particles/units/heroes/hero_vengeful/vengeful_nether_swap.vpcf", PATTACH_ABSORIGIN_FOLLOW, hCaster) 87 | ParticleManager:SetParticleControlEnt(nCasterFX, 1, hTarget, PATTACH_ABSORIGIN_FOLLOW, nil, hTarget:GetOrigin(), false) 88 | ParticleManager:ReleaseParticleIndex(nCasterFX) 89 | 90 | local nTargetFX = ParticleManager:CreateParticle("particles/units/heroes/hero_vengeful/vengeful_nether_swap_target.vpcf", PATTACH_ABSORIGIN_FOLLOW, hTarget) 91 | ParticleManager:SetParticleControlEnt(nTargetFX, 1, hCaster, PATTACH_ABSORIGIN_FOLLOW, nil, hCaster:GetOrigin(), false) 92 | ParticleManager:ReleaseParticleIndex(nTargetFX) 93 | 94 | EmitSoundOn("Hero_VengefulSpirit.NetherSwap", hCaster) 95 | EmitSoundOn("Hero_VengefulSpirit.NetherSwap", hTarget) 96 | 97 | hCaster:StartGesture(ACT_DOTA_CHANNEL_END_ABILITY_4) 98 | end -------------------------------------------------------------------------------- /game/scripts/vscripts/heroes/vengefulspirit/wave_of_terror.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | ModDota AbilityLuaSpellLibrary implementation for Wave of Terror 3 | 4 | Mechanics correct as at 7.07c (excluding talents) 5 | --]] 6 | 7 | LinkLuaModifier("modifier_vengefulspirit_wave_of_terror_lua", "heroes/vengefulspirit/wave_of_terror", LUA_MODIFIER_MOTION_NONE) 8 | 9 | --[[ 10 | ================================ 11 | ===== Wave of Terror Effect ==== 12 | ================================ 13 | --]] 14 | ---@class modifier_vengefulspirit_wave_of_terror_lua : CDOTA_Modifier_Lua 15 | ---@field armor_reduction number 16 | modifier_vengefulspirit_wave_of_terror_lua = class({}) 17 | 18 | ---@override 19 | function modifier_vengefulspirit_wave_of_terror_lua:IsDebuff() 20 | return true 21 | end 22 | 23 | ---@override 24 | function modifier_vengefulspirit_wave_of_terror_lua:GetEffectName() 25 | return "particles/units/heroes/hero_vengeful/vengeful_wave_of_terror_recipient.vpcf" 26 | end 27 | 28 | ---@override 29 | function modifier_vengefulspirit_wave_of_terror_lua:GetEffectAttachType() 30 | return PATTACH_ABSORIGIN_FOLLOW 31 | end 32 | 33 | ---@override 34 | function modifier_vengefulspirit_wave_of_terror_lua:OnCreated() 35 | self.armor_reduction = self:GetAbility():GetSpecialValueFor("armor_reduction") 36 | end 37 | 38 | ---@override 39 | function modifier_vengefulspirit_wave_of_terror_lua:OnRefresh() 40 | self.armor_reduction = self:GetAbility():GetSpecialValueFor("armor_reduction") 41 | end 42 | 43 | ---@override 44 | function modifier_vengefulspirit_wave_of_terror_lua:DeclareFunctions() 45 | local funcs = { 46 | MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS 47 | } 48 | return funcs 49 | end 50 | 51 | ---@override 52 | function modifier_vengefulspirit_wave_of_terror_lua:GetModifierPhysicalArmorBonus() 53 | return self.armor_reduction 54 | end 55 | 56 | 57 | --[[ 58 | =============================== 59 | ===== Wave of Terror Spell ==== 60 | =============================== 61 | --]] 62 | ---@class vengeful_wave_of_terror : CDOTA_Ability_Lua 63 | ---@field wave_speed number 64 | ---@field wave_width number 65 | ---@field vision_aoe number 66 | ---@field vision_duration number 67 | ---@field tooltip_duration number 68 | ---@field wave_damage number 69 | ---@field flVisionTimer number 70 | ---@field flLastThinkTime number 71 | ---@field nProjID ProjectileID 72 | vengefulspirit_wave_of_terror_lua = class({}) 73 | 74 | ---@override 75 | function vengefulspirit_wave_of_terror_lua:OnSpellStart() 76 | local vDirection = self:GetCursorPosition() - self:GetCaster():GetOrigin() 77 | vDirection = vDirection:Normalized() 78 | 79 | self.wave_speed = self:GetSpecialValueFor("wave_speed") 80 | self.wave_width = self:GetSpecialValueFor("wave_width") 81 | self.vision_aoe = self:GetSpecialValueFor("vision_aoe") 82 | self.vision_duration = self:GetSpecialValueFor("vision_duration") 83 | self.tooltip_duration = self:GetSpecialValueFor("tooltip_duration") 84 | self.wave_damage = self:GetSpecialValueFor("wave_damage") 85 | 86 | local info = { 87 | EffectName = "particles/units/heroes/hero_vengeful/vengeful_wave_of_terror.vpcf", 88 | Ability = self, 89 | vSpawnOrigin = self:GetCaster():GetOrigin(), 90 | fStartRadius = self.wave_width, 91 | fEndRadius = self.wave_width, 92 | vVelocity = vDirection * self.wave_speed, 93 | fDistance = self:GetCastRange(self:GetCaster():GetOrigin(), self:GetCaster()), 94 | Source = self:GetCaster(), 95 | iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY, 96 | iUnitTargetType = DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP, 97 | iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 98 | bProvidesVision = true, 99 | iVisionTeamNumber = self:GetCaster():GetTeamNumber(), 100 | iVisionRadius = self.vision_aoe 101 | } 102 | 103 | self.flVisionTimer = self.wave_width / self.wave_speed 104 | self.flLastThinkTime = GameRules:GetGameTime() 105 | self.nProjID = ProjectileManager:CreateLinearProjectile(info) 106 | EmitSoundOn("Hero_VengefulSpirit.WaveOfTerror" , self:GetCaster()) 107 | end 108 | 109 | ---@override 110 | function vengefulspirit_wave_of_terror_lua:OnProjectileThink(vLocation) 111 | self.flVisionTimer = self.flVisionTimer - (GameRules:GetGameTime() - self.flLastThinkTime) 112 | 113 | if self.flVisionTimer <= 0.0 then 114 | local vVelocity = ProjectileManager:GetLinearProjectileVelocity(self.nProjID) 115 | AddFOWViewer(self:GetCaster():GetTeamNumber(), vLocation + vVelocity * ( self.wave_width / self.wave_speed ), self.vision_aoe, self.vision_duration, false) 116 | self.flVisionTimer = self.wave_width / self.wave_speed 117 | end 118 | end 119 | 120 | ---@override 121 | function vengefulspirit_wave_of_terror_lua:OnProjectileHit(hTarget, vLocation) 122 | if hTarget ~= nil then 123 | local damage = { 124 | victim = hTarget, 125 | attacker = self:GetCaster(), 126 | damage = self.wave_damage, 127 | damage_type = DAMAGE_TYPE_PURE, 128 | ability = self 129 | } 130 | 131 | ApplyDamage(damage) --TODO: Type annotate ApplyDamage to take table not handle 132 | 133 | --TODO: Figure out why IntelliJ is convinced AddNewModifier does not exist 134 | hTarget:AddNewModifier(self:GetCaster(), self, "modifier_vengefulspirit_wave_of_terror_lua", { duration = self.tooltip_duration }) 135 | end 136 | 137 | return false 138 | end --------------------------------------------------------------------------------