Skip to content

Commit b817f8e

Browse files
authored
Merge pull request #24 from evaldobratti/master
ward killer
2 parents d853f78 + a09d4c9 commit b817f8e

File tree

5 files changed

+299
-45
lines changed

5 files changed

+299
-45
lines changed

src/main/java/opendota/Parse.java

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
import opendota.combatlogvisitors.TrackVisitor;
3939
import opendota.combatlogvisitors.GreevilsGreedVisitor;
4040
import opendota.combatlogvisitors.TrackVisitor.TrackStatus;
41+
import opendota.processors.warding.OnWardExpired;
42+
import opendota.processors.warding.OnWardKilled;
43+
import opendota.processors.warding.OnWardPlaced;
4144

4245
public class Parse {
4346

@@ -401,7 +404,6 @@ else if (cle.getType() == DOTA_COMBATLOG_TYPES.DOTA_COMBATLOG_XP) {
401404

402405
@OnEntityEntered
403406
public void onEntityEntered(Context ctx, Entity e) {
404-
processEntity(ctx, e, false);
405407
if (e.getDtClass().getDtName().equals("CDOTAWearableItem")) {
406408
Integer accountId = getEntityProperty(e, "m_iAccountID", null);
407409
Integer itemDefinitionIndex = getEntityProperty(e, "m_iItemDefinitionIndex", null);
@@ -418,11 +420,6 @@ public void onEntityEntered(Context ctx, Entity e) {
418420
}
419421
}
420422
}
421-
422-
@OnEntityLeft
423-
public void onEntityLeft(Context ctx, Entity e) {
424-
processEntity(ctx, e, true);
425-
}
426423

427424
@UsesStringTable("EntityNames")
428425
@UsesEntities
@@ -727,45 +724,43 @@ public <T> T getEntityProperty(Entity e, String property, Integer idx) {
727724
}
728725
}
729726

730-
public void processEntity(Context ctx, Entity e, boolean entityLeft)
731-
{
732-
//CDOTA_NPC_Observer_Ward
733-
//CDOTA_NPC_Observer_Ward_TrueSight
734-
//s1 "DT_DOTA_NPC_Observer_Ward"
735-
//s1 "DT_DOTA_NPC_Observer_Ward_TrueSight"
736-
boolean isObserver = e.getDtClass().getDtName().equals("CDOTA_NPC_Observer_Ward");
737-
boolean isSentry = e.getDtClass().getDtName().equals("CDOTA_NPC_Observer_Ward_TrueSight");
738-
if (isObserver || isSentry) {
739-
//System.err.println(e);
740-
Entry entry = new Entry(time);
741-
Integer x = getEntityProperty(e, "CBodyComponent.m_cellX", null);
742-
Integer y = getEntityProperty(e, "CBodyComponent.m_cellY", null);
743-
Integer z = getEntityProperty(e, "CBodyComponent.m_cellZ", null);
744-
Integer[] pos = {x, y};
745-
entry.x = x;
746-
entry.y = y;
747-
entry.z = z;
748-
if (entityLeft)
749-
{
750-
entry.type = isObserver ? "obs_left" : "sen_left";
751-
}
752-
else
753-
{
754-
entry.type = isObserver ? "obs" : "sen";
755-
}
756-
entry.key = Arrays.toString(pos);
757-
entry.entityleft = entityLeft;
758-
entry.ehandle = e.getHandle();
759-
//System.err.println(entry.key);
760-
Integer owner = getEntityProperty(e, "m_hOwnerEntity", null);
761-
Entity ownerEntity = ctx.getProcessor(Entities.class).getByHandle(owner);
762-
entry.slot = ownerEntity != null ? (Integer) getEntityProperty(ownerEntity, "m_iPlayerID", null) : ward_ehandle_to_slot.get(entry.ehandle);
763-
if (entry.slot != null && !ward_ehandle_to_slot.containsKey(entry.ehandle)) {
764-
ward_ehandle_to_slot.put(entry.ehandle, entry.slot);
765-
}
766-
//2/3 radiant/dire
767-
//entry.team = e.getProperty("m_iTeamNum");
768-
output(entry);
727+
@OnWardKilled
728+
public void onWardKilled(Context ctx, Entity e, String killerHeroName) {
729+
Entry wardEntry = buildWardEntry(ctx, e);
730+
wardEntry.attackername = killerHeroName;
731+
output(wardEntry);
732+
}
733+
734+
@OnWardExpired
735+
@OnWardPlaced
736+
public void onWardExistenceChanged(Context ctx, Entity e) {
737+
output(buildWardEntry(ctx, e));
738+
}
739+
740+
private Entry buildWardEntry(Context ctx, Entity e) {
741+
Entry entry = new Entry(time);
742+
boolean isObserver = !e.getDtClass().getDtName().contains("TrueSight");
743+
Integer x = getEntityProperty(e, "CBodyComponent.m_cellX", null);
744+
Integer y = getEntityProperty(e, "CBodyComponent.m_cellY", null);
745+
Integer z = getEntityProperty(e, "CBodyComponent.m_cellZ", null);
746+
Integer life_state = getEntityProperty(e, "m_lifeState", null);
747+
Integer[] pos = {x, y};
748+
entry.x = x;
749+
entry.y = y;
750+
entry.z = z;
751+
entry.type = isObserver ? "obs" : "sen";
752+
entry.entityleft = life_state == 1;
753+
entry.key = Arrays.toString(pos);
754+
entry.ehandle = e.getHandle();
755+
756+
if (entry.entityleft) {
757+
entry.type += "_left";
769758
}
759+
760+
Integer owner = getEntityProperty(e, "m_hOwnerEntity", null);
761+
Entity ownerEntity = ctx.getProcessor(Entities.class).getByHandle(owner);
762+
entry.slot = ownerEntity != null ? (Integer) getEntityProperty(ownerEntity, "m_iPlayerID", null) : null;
763+
764+
return entry;
770765
}
771766
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package opendota.processors.warding;
2+
3+
import java.lang.annotation.Annotation;
4+
5+
import skadistats.clarity.event.UsagePointMarker;
6+
import skadistats.clarity.event.UsagePointType;
7+
import skadistats.clarity.model.Entity;
8+
9+
import java.lang.annotation.ElementType;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Target(value = ElementType.METHOD)
16+
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class })
17+
public @interface OnWardExpired {
18+
}
19+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package opendota.processors.warding;
2+
3+
import java.lang.annotation.Annotation;
4+
5+
import skadistats.clarity.event.UsagePointMarker;
6+
import skadistats.clarity.event.UsagePointType;
7+
import skadistats.clarity.model.Entity;
8+
9+
import java.lang.annotation.ElementType;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Target(value = ElementType.METHOD)
16+
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class, String.class })
17+
public @interface OnWardKilled {
18+
}
19+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package opendota.processors.warding;
2+
3+
import java.lang.annotation.Annotation;
4+
5+
import skadistats.clarity.event.UsagePointMarker;
6+
import skadistats.clarity.event.UsagePointType;
7+
import skadistats.clarity.model.Entity;
8+
9+
import java.lang.annotation.ElementType;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Target(value = ElementType.METHOD)
16+
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class })
17+
public @interface OnWardPlaced {
18+
}
19+
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package opendota.processors.warding;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Collections;
5+
import java.util.HashMap;
6+
import java.util.HashSet;
7+
import java.util.Map;
8+
import java.util.Queue;
9+
import java.util.Set;
10+
11+
import skadistats.clarity.event.Event;
12+
import skadistats.clarity.event.EventListener;
13+
import skadistats.clarity.event.Initializer;
14+
import skadistats.clarity.event.Provides;
15+
import skadistats.clarity.model.Entity;
16+
import skadistats.clarity.model.FieldPath;
17+
import skadistats.clarity.model.CombatLogEntry;
18+
import skadistats.clarity.processor.entities.OnEntityCreated;
19+
import skadistats.clarity.processor.entities.OnEntityDeleted;
20+
import skadistats.clarity.processor.entities.OnEntityUpdated;
21+
import skadistats.clarity.processor.entities.UsesEntities;
22+
import skadistats.clarity.processor.gameevents.OnCombatLogEntry;
23+
import skadistats.clarity.processor.reader.OnTickEnd;
24+
import skadistats.clarity.processor.runner.Context;
25+
import skadistats.clarity.wire.common.proto.DotaUserMessages;
26+
27+
/**
28+
* @author micaelbergeron
29+
*/
30+
@UsesEntities
31+
@Provides({ OnWardKilled.class, OnWardExpired.class, OnWardPlaced.class })
32+
public class Wards {
33+
34+
private static final Map<String, String> WARDS_TARGET_NAME_BY_DT_CLASS;
35+
private static final Set<String> WARDS_DT_CLASSES;
36+
private static final Set<String> WARDS_TARGET_NAMES;
37+
38+
static {
39+
final String TARGET_NAME_OBSERVER = "npc_dota_observer_wards";
40+
final String TARGET_NAME_SENTRY = "npc_dota_sentry_wards";
41+
HashMap<String, String> target_by_dtclass = new HashMap<>(4);
42+
43+
target_by_dtclass.put("DT_DOTA_NPC_Observer_Ward", TARGET_NAME_OBSERVER);
44+
target_by_dtclass.put("CDOTA_NPC_Observer_Ward", TARGET_NAME_OBSERVER);
45+
target_by_dtclass.put("DT_DOTA_NPC_Observer_Ward_TrueSight", TARGET_NAME_SENTRY);
46+
target_by_dtclass.put("CDOTA_NPC_Observer_Ward_TrueSight", TARGET_NAME_SENTRY);
47+
48+
WARDS_TARGET_NAME_BY_DT_CLASS = Collections.unmodifiableMap(target_by_dtclass);
49+
WARDS_DT_CLASSES = Collections.unmodifiableSet(target_by_dtclass.keySet());
50+
WARDS_TARGET_NAMES = Collections.unmodifiableSet(new HashSet<>(target_by_dtclass.values()));
51+
}
52+
53+
private final Map<Integer, FieldPath> lifeStatePaths = new HashMap<>();
54+
private final Map<Integer, Integer> currentLifeState = new HashMap<>();
55+
56+
private final Map<String, Queue<String>> wardKillersByWardClass = new HashMap<>();
57+
private Queue<ProcessEntityCommand> toProcess = new ArrayDeque<>();
58+
59+
private Event<OnWardKilled> evKilled;
60+
private Event<OnWardExpired> evExpired;
61+
private Event<OnWardPlaced> evPlaced;
62+
63+
private class ProcessEntityCommand {
64+
65+
private final Entity entity;
66+
private final FieldPath fieldPath;
67+
68+
public ProcessEntityCommand(Entity e, FieldPath p) {
69+
entity = e;
70+
fieldPath = p;
71+
}
72+
}
73+
74+
@Initializer(OnWardKilled.class)
75+
public void initOnWardKilled(final Context ctx, final EventListener<OnWardKilled> listener) {
76+
evKilled = ctx.createEvent(OnWardKilled.class, Entity.class, String.class);
77+
}
78+
79+
@Initializer(OnWardExpired.class)
80+
public void initOnWardExpired(final Context ctx, final EventListener<OnWardExpired> listener) {
81+
evExpired = ctx.createEvent(OnWardExpired.class, Entity.class);
82+
}
83+
84+
@Initializer(OnWardPlaced.class)
85+
public void initOnWardPlaced(final Context ctx, final EventListener<OnWardPlaced> listener) {
86+
evPlaced = ctx.createEvent(OnWardPlaced.class, Entity.class);
87+
}
88+
89+
public Wards() {
90+
WARDS_TARGET_NAMES.forEach((cls) -> {
91+
wardKillersByWardClass.put(cls, new ArrayDeque<>());
92+
});
93+
}
94+
95+
@OnEntityCreated
96+
public void onCreated(Context ctx, Entity e) {
97+
if (!isWard(e)) return;
98+
99+
FieldPath lifeStatePath;
100+
101+
clearCachedState(e);
102+
ensureFieldPathForEntityInitialized(e);
103+
if ((lifeStatePath = getFieldPathForEntity(e)) != null) {
104+
processLifeStateChange(e, lifeStatePath);
105+
}
106+
}
107+
108+
@OnEntityUpdated
109+
public void onUpdated(Context ctx, Entity e, FieldPath[] fieldPaths, int num) {
110+
FieldPath p;
111+
if ((p = getFieldPathForEntity(e)) != null) {
112+
for (int i = 0; i < num; i++) {
113+
if (fieldPaths[i].equals(p)) {
114+
toProcess.add(new ProcessEntityCommand(e, p));
115+
break;
116+
}
117+
}
118+
}
119+
}
120+
121+
@OnEntityDeleted
122+
public void onDeleted(Context ctx, Entity e) {
123+
clearCachedState(e);
124+
}
125+
126+
@OnCombatLogEntry
127+
public void onCombatLogEntry(Context ctx, CombatLogEntry entry) {
128+
if (!isWardDeath(entry)) return;
129+
130+
String killer;
131+
if ((killer = entry.getDamageSourceName()) != null) {
132+
wardKillersByWardClass.get(entry.getTargetName()).add(killer);
133+
}
134+
}
135+
136+
@OnTickEnd
137+
public void onTickEnd(Context ctx, boolean synthetic) {
138+
if (!synthetic) return;
139+
140+
ProcessEntityCommand cmd;
141+
while ((cmd = toProcess.poll()) != null) {
142+
processLifeStateChange(cmd.entity, cmd.fieldPath);
143+
}
144+
}
145+
146+
private FieldPath getFieldPathForEntity(Entity e) {
147+
return lifeStatePaths.get(e.getDtClass().getClassId());
148+
}
149+
150+
private void clearCachedState(Entity e) {
151+
currentLifeState.remove(e.getIndex());
152+
}
153+
154+
private void ensureFieldPathForEntityInitialized(Entity e) {
155+
Integer cid = e.getDtClass().getClassId();
156+
if (!lifeStatePaths.containsKey(cid)) {
157+
lifeStatePaths.put(cid, e.getDtClass().getFieldPathForName("m_lifeState"));
158+
}
159+
}
160+
161+
private boolean isWard(Entity e) {
162+
return WARDS_DT_CLASSES.contains(e.getDtClass().getDtName());
163+
}
164+
165+
private boolean isWardDeath(CombatLogEntry e) {
166+
return e.getType().equals(DotaUserMessages.DOTA_COMBATLOG_TYPES.DOTA_COMBATLOG_DEATH)
167+
&& WARDS_TARGET_NAMES.contains(e.getTargetName());
168+
}
169+
170+
public void processLifeStateChange(Entity e, FieldPath p) {
171+
int oldState = currentLifeState.containsKey(e.getIndex()) ? currentLifeState.get(e.getIndex()) : 2;
172+
int newState = e.getPropertyForFieldPath(p);
173+
if (oldState != newState) {
174+
switch(newState) {
175+
case 0:
176+
if (evPlaced != null) {
177+
evPlaced.raise(e);
178+
}
179+
break;
180+
case 1:
181+
String killer;
182+
if ((killer = wardKillersByWardClass.get(getWardTargetName(e.getDtClass().getDtName())).poll()) != null) {
183+
if (evKilled != null) {
184+
evKilled.raise(e, killer);
185+
}
186+
} else {
187+
if (evExpired != null) {
188+
evExpired.raise(e);
189+
}
190+
}
191+
break;
192+
}
193+
}
194+
195+
currentLifeState.put(e.getIndex(), newState);
196+
}
197+
198+
private String getWardTargetName(String ward_dtclass_name) {
199+
return WARDS_TARGET_NAME_BY_DT_CLASS.get(ward_dtclass_name);
200+
}
201+
}
202+

0 commit comments

Comments
 (0)