package de.lighti.parsing; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.logging.Logger; import de.lighti.DefaultGameEventListener; import de.lighti.model.AppState; import de.lighti.model.Entity; import de.lighti.model.Property; import de.lighti.model.game.Ability; import de.lighti.model.game.Dota2Item; import de.lighti.model.game.Hero; import de.lighti.model.state.ParseState; public class HeroTracker extends DefaultGameEventListener { private final AppState state; private final Map<Hero, TreeMap<Long, Integer[]>> itemCache; private final Map<Hero, TreeMap<Long, Set<Integer>>> abilityCache; public HeroTracker( AppState state ) { super(); this.state = state; itemCache = new HashMap<Hero, TreeMap<Long, Integer[]>>(); abilityCache = new HashMap<Hero, TreeMap<Long, Set<Integer>>>(); } @Override public void entityCreated( long tickMs, Entity e ) { if (e.getEntityClass().getName().contains( "CDOTA_Unit_Hero_" )) { if (state.getHero( e.getId() ) == null) { state.setHero( e.getId(), new Hero( AppState.getHeroName( e.getEntityClass().getName() ) ) ); } for (final Property p : e.getProperties()) { final String name = p.getName(); if (name.contains( "m_hAbilities" )) { int value = (int) e.getProperty( name ).getValue(); if (value != 0x1FFFFF) { final Hero h = state.getHero( e.getId() ); final int slot = Integer.parseInt( name.substring( name.lastIndexOf( "." ) + 1 ) ); value &= 0x7ff; // h.addAbility( DotaPlay.getTickMs(), slot, value ); final Ability a = state.getAbility( tickMs, value ); if (a == null) { Logger.getLogger( getClass().getName() ).warning( "Hero " + h.getName() + " has an odd ability" ); //Most likely a temporary entity } else { h.getAbilities().add( a ); } } } } } } @Override public <T> void entityUpdated( long tickMs, Entity e, String name, T oldValue ) { if (e.getEntityClass().getDtName().contains( "DT_DOTA_Unit_Hero" )) { final Hero h = state.getHero( e.getId() ); if (name.equals( "DT_DOTA_BaseNPC.m_cellX" )) { state.getHero( e.getId() ).addX( tickMs, (Integer) e.getProperty( name ).getValue() ); } else if (name.equals( "DT_DOTA_BaseNPC.m_cellY" )) { state.getHero( e.getId() ).addY( tickMs, (Integer) e.getProperty( name ).getValue() ); } else if (name.contains( "m_hItems" )) { final int slot = Integer.parseInt( name.substring( name.lastIndexOf( "." ) + 1 ) ); int value = (int) e.getProperty( name ).getValue(); if (value != 0x1FFFFF) { value &= 0x7ff; // h.setItem( DotaPlay.getTickMs(), slot, value ); setItemInCache( h, tickMs, slot, value ); } else { // h.setItem( DotaPlay.getTickMs(), slot, null ); setItemInCache( h, tickMs, slot, null ); } } else if (name.contains( "m_hAbilities" )) { int value = (int) e.getProperty( name ).getValue(); if (value != 0x1FFFFF) { // final int slot = Integer.parseInt( name.substring( name.lastIndexOf( "." ) + 1 ) ); value &= 0x7ff; // final Ability a = state.getAbility( tickMs, value ); // if (a == Ability.UNKNOWN_ABILITY) { // Logger.getLogger( getClass().getName() ).warning( "Hero " + h.getName() + " has an odd ability" ); //Most likely a temporary entity // } // else { // h.getAbilities().add( a ); // } setAbilityInCache( h, tickMs, value ); } } } } @Override public void parseComplete( long tickMs, ParseState state ) { super.parseComplete( tickMs, state ); //Items for (final Hero h : itemCache.keySet()) { final TreeMap<Long, Integer[]> heroItems = itemCache.get( h ); for (final Long l : heroItems.keySet()) { final Integer[] frameItems = heroItems.get( l ); for (int slot = 0; slot < frameItems.length; slot++) { if (frameItems[slot] != null) { final Dota2Item i = this.state.getItem( l, frameItems[slot] ); h.setItem( l, slot, i ); } else { h.setItem( l, slot, null ); } } } } //Abilities for (final Hero h : abilityCache.keySet()) { final TreeMap<Long, Set<Integer>> heroAbilities = abilityCache.get( h ); for (final Entry<Long, Set<Integer>> e : heroAbilities.entrySet()) { for (final Integer value : e.getValue()) { final Ability a = this.state.getAbility( e.getKey(), value ); if (a == Ability.UNKNOWN_ABILITY) { Logger.getLogger( getClass().getName() ).warning( "Hero " + h.getName() + " has an odd ability " + value ); //Most likely a temporary entity } else { h.getAbilities().add( a ); } } } } abilityCache.clear(); itemCache.clear(); } private void setAbilityInCache( Hero h, long tickMs, int value ) { TreeMap<Long, Set<Integer>> abilities = abilityCache.get( h ); if (abilities == null) { abilities = new TreeMap<Long, Set<Integer>>(); abilities.put( 0l, new HashSet() ); abilityCache.put( h, abilities ); } if (abilities.containsKey( tickMs )) { //Just store the update abilities.get( tickMs ).add( value ); } else { //We advanced. Push the current bag configuration, calculate the diff to the previous one and make //a new array for the new tick final Entry<Long, Set<Integer>> current = abilities.floorEntry( tickMs ); final Set<Integer> newBag = new HashSet( current.getValue() ); newBag.add( value ); abilities.put( tickMs, newBag ); } } private void setItemInCache( Hero h, long tickMs, int slot, Integer value ) { TreeMap<Long, Integer[]> items = itemCache.get( h ); if (items == null) { items = new TreeMap<Long, Integer[]>(); items.put( 0l, new Integer[Hero.BAG_SIZE] ); itemCache.put( h, items ); } if (items.containsKey( tickMs )) { //Just store the update items.get( tickMs )[slot] = value; } else { //We advanced. Push the current bag configuration, calculate the diff to the previous one and make //a new array for the new tick final Entry<Long, Integer[]> current = items.floorEntry( tickMs ); final Integer[] newBag = Arrays.copyOf( current.getValue(), current.getValue().length ); newBag[slot] = value; items.put( tickMs, newBag ); } } }