├── requirements.txt ├── .gitignore ├── Exo-SemiBold.ttf ├── gameinfo_edit.png ├── showcase_spellicons.png ├── prerendered_items ├── dagon.png ├── dagon_2.png ├── dagon_3.png ├── dagon_4.png ├── dagon_5.png ├── power_treads.png ├── diffusal_blade.png ├── diffusal_blade_2.png ├── power_treads_agi.png ├── power_treads_int.png └── power_treads_str.png ├── showcase_items_combined.png ├── showcase_items_manacost.png ├── README.md └── render_icons.py /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow>=3.4.1 2 | vdf>=2.1 3 | vpk>=0.12 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | src/items 4 | src/spellicons 5 | out/ 6 | -------------------------------------------------------------------------------- /Exo-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/Exo-SemiBold.ttf -------------------------------------------------------------------------------- /gameinfo_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/gameinfo_edit.png -------------------------------------------------------------------------------- /showcase_spellicons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/showcase_spellicons.png -------------------------------------------------------------------------------- /prerendered_items/dagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/dagon.png -------------------------------------------------------------------------------- /showcase_items_combined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/showcase_items_combined.png -------------------------------------------------------------------------------- /showcase_items_manacost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/showcase_items_manacost.png -------------------------------------------------------------------------------- /prerendered_items/dagon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/dagon_2.png -------------------------------------------------------------------------------- /prerendered_items/dagon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/dagon_3.png -------------------------------------------------------------------------------- /prerendered_items/dagon_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/dagon_4.png -------------------------------------------------------------------------------- /prerendered_items/dagon_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/dagon_5.png -------------------------------------------------------------------------------- /prerendered_items/power_treads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/power_treads.png -------------------------------------------------------------------------------- /prerendered_items/diffusal_blade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/diffusal_blade.png -------------------------------------------------------------------------------- /prerendered_items/diffusal_blade_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/diffusal_blade_2.png -------------------------------------------------------------------------------- /prerendered_items/power_treads_agi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/power_treads_agi.png -------------------------------------------------------------------------------- /prerendered_items/power_treads_int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/power_treads_int.png -------------------------------------------------------------------------------- /prerendered_items/power_treads_str.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rossengeorgiev/dota2mod_manacost/HEAD/prerendered_items/power_treads_str.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dota 7.00 2 | 3 | The New Journey patch has landed and with it mana cost display for items. 4 | This spells the end for this project. 5 | I would like to thank everyone who used mod and best of luck in your games. 6 | 7 | ---- 8 | 9 | # ! NOTICE ! 10 | 11 | While the current method works, note that it has not been allowed explicitly. 12 | It's unlikely that you will get VAC'd, but there are no guarantees. Use at your own risk. 13 | 14 | Guide won't work when using the mod. No workaround available at the moment. 15 | 16 | ### Dota 2 mod — Mana cost for items 17 | ---- 18 | [▶ Download latest version](https://github.com/rossengeorgiev/dota2mod_manacost/releases) 19 |        20 | [▶ Issues & suggestions](https://github.com/rossengeorgiev/dota2mod_manacost/issues) 21 | 22 | ---- 23 | 24 | ![Item manacost showcase](showcase_items_manacost.png "Item mana cost showcase") 25 | 26 | This mod adds mana/health cost to the icons of in-game items. 27 | There are additional modification to upgradeable items and Power threads, indicating their level or the next stat. 28 | 29 | The prerendered image modifications were made by Reddit user **/u/lerobotsexy**, who also originally came up with the idea for this mod. 30 | 31 | 32 | #### Item gold cost variant 33 | 34 | Adds item gold cost. Useful for quickly estimating networth of enemy heroes. Below is a preview of the combined version. 35 | 36 | ![Item manacost showcase](showcase_items_combined.png "Item gold cost showcase") 37 | 38 | 39 | #### Ability damage type mod 40 | 41 | Color codes the ability damage type in the top right corner. Physical/Magic/Pure are respectively Red/Blue/Purple. 42 | 43 | ![Spellicons showcase](showcase_spellicons.png "Spellicons showcase") 44 | 45 | ---- 46 | 47 | The repo contains a bunch of scripts to automatically generate the images based on data included with the game. 48 | This way the icon set can quickly be regenerated in the case of a game patch. 49 | 50 | ## Installation 51 | 52 | 1. Download the latest `mod_xxxxxxxx.zip` from the [release section](https://github.com/rossengeorgiev/dota2mod_manacost/releases) 53 | 2. Extract in `[steam folder]\steamapps\common\dota 2 beta\game\` 54 | 3. Edit `[dota 2 beta dir]\game\dota\gameinfo.gi` and add `Game costmod_xxxxxxx` in the `SearchPaths` section 55 | 56 | ![Editing gameinfo.gi](gameinfo_edit.png "Editing gameinfo.gi") 57 | 58 | ## Building 59 | 60 | All code in this repo assumes that the environment is either `Linux` or `cygwin`. 61 | 62 | ### Required tools 63 | * Python & pip 64 | * `optional` zip 65 | * `optional` ImageMagik 66 | 67 | ### Steps 68 | 1. `pip install -r requirements.txt` 69 | 2. Set `vpk_path` in `render_icons.py` 70 | 3. `python render_icons.py`. Renders all images to `./out/` 71 | 4. `optional` `build_release.sh` Create zip file releases 72 | 73 | -------------------------------------------------------------------------------- /render_icons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import errno 5 | import fnmatch 6 | import shutil 7 | import logging 8 | 9 | logging.basicConfig(format='[%(asctime)s] %(levelname)s %(name)s: %(message)s', level=logging.INFO) 10 | LOG = logging.getLogger() 11 | 12 | from PIL import Image, ImageFont, ImageDraw 13 | import vdf 14 | import vpk 15 | 16 | # configs 17 | vpk_path = '/d/Steam/steamapps/common/dota 2 beta/game/dota/pak01_dir.vpk' 18 | vpk_img_root = 'resource/flash3/images' 19 | fill_blue = "#1f496f" 20 | fill_green = "#26721b" 21 | fill_gold = "#7a4b4e" 22 | font = ImageFont.truetype('Exo-SemiBold.ttf', 13) 23 | src_root = "./src/" 24 | src_preimages_root = "./prerendered_items/" 25 | out_root = "./out/" 26 | 27 | 28 | # helper functions 29 | def mktree(path): 30 | dirpath = os.path.join(*os.path.split(path)[:-1]) 31 | try: 32 | os.makedirs(dirpath) 33 | except OSError as exc: 34 | if exc.errno == errno.EEXIST and os.path.isdir(dirpath): 35 | pass 36 | return path 37 | 38 | # clear output directory 39 | if os.path.exists("./out"): 40 | shutil.rmtree("./out") 41 | 42 | LOG.info("Reading VPK from %s" % repr(vpk_path)) 43 | 44 | # load game asset index 45 | pak1 = vpk.open(vpk_path) 46 | 47 | LOG.info("Reading items.txt") 48 | 49 | # load items.txt schema and save a local copy to track in repo 50 | with pak1.get_file("scripts/npc/items.txt") as vpkfile: 51 | vpkfile.save(os.path.join(src_root, 'items.txt')) 52 | items = vdf.load(vpkfile)['DOTAAbilities'] 53 | 54 | pt_names = ['power_treads_str.png', 'power_treads_agi.png', 'power_treads_int.png'] 55 | 56 | def get_file(filename): 57 | prerendered_path = os.path.join(src_preimages_root, filename) 58 | 59 | if os.path.exists(prerendered_path): 60 | return open(prerendered_path, 'rb') 61 | else: 62 | return pak1.get_file('/'.join([vpk_img_root, "items", filename])) 63 | 64 | LOG.info("Generating images for %d items" % len(items)) 65 | # find all items with mana or health requirements 66 | for item_name in items: 67 | 68 | if type(items[item_name]) is not dict: 69 | continue 70 | 71 | item = items[item_name] 72 | 73 | filename = "%s.png" % item_name[5:] # removes 'item_' prefix 74 | 75 | # skip recipies, since they all sahre the same icon image 76 | if filename.startswith("recipe_"): 77 | continue 78 | 79 | # it done like so to handle power treads special case 80 | filenames = [filename] 81 | 82 | if filename == 'power_treads.png': 83 | filenames += pt_names 84 | 85 | image_src_files = map(get_file, filenames) 86 | 87 | manacost_img = None 88 | color = fill_blue 89 | # generate manacost banner 90 | if 'AbilityManaCost' in item: 91 | manacost = item['AbilityManaCost'] 92 | 93 | # some items require 0 mana, like 'blink dagger' or 'soul ring' 94 | if manacost == '0': 95 | # ok, we look if the items sacrifice health, e.g. soul ring 96 | try: 97 | for xv in item['AbilitySpecial'].itervalues(): 98 | if type(xv) is not dict: 99 | continue 100 | 101 | if "health_sacrifice" in xv: 102 | manacost = xv['health_sacrifice'] 103 | raise Exception 104 | except: 105 | color = fill_green # sacrifice health, health is green, we use green 106 | 107 | # handle items that are upgradable (manacosts for each level is separated by whitespaces) 108 | else: 109 | if ' ' in manacost: 110 | manacosts = manacost.split(' ') 111 | 112 | try: 113 | idx = int(item_name[-1]) - 1 # e.g. item_dagon, item_dagon_2, item_dagon_3 114 | except: 115 | idx = 0 116 | 117 | manacost = manacosts[idx] 118 | 119 | if manacost != '0': 120 | # center text in our little box 121 | text_pos_padding = (3 - len(manacost)) * 4 122 | 123 | # open the image 124 | 125 | for filename, image_src_file in zip(filenames, image_src_files): 126 | manacost_img = Image.open(image_src_file) 127 | 128 | # add info in the bottom left corner 129 | d = ImageDraw.Draw(manacost_img) 130 | d.polygon([(0, 50), (20, 50), (25, 64), (0, 64)], fill=color) 131 | d.text((text_pos_padding, 49), manacost, font=font, fill="#ffffff") 132 | del d 133 | 134 | # save the image in the ouput directory 135 | savepath = os.path.join(out_root, 'manacost', vpk_img_root, 'items', filename) 136 | manacost_img.save(mktree(savepath)) 137 | 138 | # generate itemcost banner 139 | if 'ItemCost' not in item or item['ItemCost'] == '0': 140 | continue 141 | 142 | for filename, image_src_file in zip(filenames, image_src_files): 143 | image_src_file.seek(0) 144 | itemcost_img = Image.open(image_src_file) 145 | 146 | pad = 8 * len(item['ItemCost']) 147 | 148 | d = ImageDraw.Draw(itemcost_img) 149 | d.polygon([(84-pad, 50), (88, 50), (88, 64), (82-pad, 64)], fill=fill_gold) 150 | d.text((86-pad, 49), item['ItemCost'], font=font, fill='#ffffff') 151 | del d 152 | 153 | savepath = os.path.join(out_root, 'goldcost', vpk_img_root, 'items', filename) 154 | itemcost_img.save(mktree(savepath)) 155 | 156 | # generate combined version if needed 157 | if manacost_img is not None: 158 | itemcost_img.paste(manacost_img.crop((0, 0, 30, 64)), box=(0, 0, 30, 64)) 159 | 160 | savepath = os.path.join(out_root, 'combined', vpk_img_root, 'items', filename) 161 | itemcost_img.save(mktree(savepath)) 162 | 163 | 164 | # add color coded damage type in the top right corner 165 | 166 | # RED physical 167 | # BLUE magic 168 | # PURPLE pure 169 | dmg_type_color = { 170 | "DAMAGE_TYPE_PHYSICAL": "#FF0000", 171 | "DAMAGE_TYPE_MAGICAL": "#007FFF", 172 | "DAMAGE_TYPE_PURE": "#DF00FF", 173 | } 174 | 175 | LOG.info("Reading npc_abilities.txt") 176 | # load npc_abilities.txt schema and save a local copy to track in repo 177 | with pak1.get_file("scripts/npc/npc_abilities.txt") as vpkfile: 178 | vpkfile.save(os.path.join(src_root, 'npc_abilities.txt')) 179 | abilities = vdf.load(vpkfile)['DOTAAbilities'] 180 | 181 | src_files = [path for path in pak1 if path.startswith("resource/flash3/images/spellicons/")] 182 | 183 | LOG.info("Generating images for %d abilities" % len(abilities)) 184 | for name in abilities: 185 | 186 | if type(abilities[name]) is not dict: 187 | continue 188 | 189 | cur = abilities[name] 190 | 191 | if 'AbilityUnitDamageType' not in cur: 192 | continue 193 | if cur['AbilityUnitDamageType'] not in dmg_type_color: 194 | continue 195 | 196 | color = dmg_type_color[cur['AbilityUnitDamageType']] 197 | 198 | for filepath in fnmatch.filter(src_files, "*%s.png" % name): 199 | img = Image.open(pak1.get_file(filepath)) 200 | 201 | d = ImageDraw.Draw(img) 202 | d.polygon([(128-42, 0), (128, 0), (128, 42)], fill="#191C22") 203 | d.polygon([(128-38, 0), (128, 0), (128, 38)], fill=color) 204 | del d 205 | 206 | # save the image in the ouput directory 207 | filepath = mktree(os.path.join(out_root, 'damagetype', filepath)) 208 | img.save(filepath) 209 | img.close() 210 | 211 | LOG.info("Generating VPKs") 212 | 213 | # create VPKs 214 | vpk.new(os.path.join(out_root, 'manacost')).save(mktree(os.path.join(out_root, 'costmod_manacost', 'pak01_dir.vpk'))) 215 | vpk.new(os.path.join(out_root, 'goldcost')).save(mktree(os.path.join(out_root, 'costmod_goldcost', 'pak01_dir.vpk'))) 216 | vpk.new(os.path.join(out_root, 'combined')).save(mktree(os.path.join(out_root, 'costmod_combined', 'pak01_dir.vpk'))) 217 | vpk.new(os.path.join(out_root, 'damagetype')).save(mktree(os.path.join(out_root, 'costmod_damagetype', 'pak01_dir.vpk'))) 218 | 219 | 220 | LOG.info("Done") 221 | --------------------------------------------------------------------------------