public AbilityEvent(BitReader bitReader, Replay replay, Player player, AbilityData abilityData, UnitData unitData) { uint flags; // 1.3.3 patch notes: // - Fixed an issue where the APM statistic could be artificially increased. // This adds the "failed" flag, which is triggered usually by holding down a // hotkey, leading to key repeat spamming the event throughout a single tick. if (replay.ReplayBuild < 18574) // < 1.3.3 { flags = bitReader.Read(17); } else if (replay.ReplayBuild < 22612) // < 1.5.0 { flags = bitReader.Read(18); } else { flags = bitReader.Read(20); } Queued = (flags & 2) != 0; RightClick = (flags & 8) != 0; WireframeClick = (flags & 0x20) != 0; ToggleAbility = (flags & 0x40) != 0; EnableAutoCast = (flags & 0x80) != 0; AbilityUsed = (flags & 0x100) != 0; WireframeUnload = (flags & 0x200) != 0; WireframeCancel = (flags & 0x400) != 0; MinimapClick = (flags & 0x10000) != 0; AbilityFailed = (flags & 0x20000) != 0; // flags & 0xf815 -> Debug for unknown flags // Never found any across all test data. DefaultAbility = (bitReader.Read(1) == 0); DefaultActor = true; if (!DefaultAbility) { AbilityType = abilityData.GetAbilityType( (int)bitReader.Read(16), (int)bitReader.Read(5)); DefaultActor = (bitReader.Read(1) == 0); if (!DefaultActor) { // I'm thinking this would be an array type... but I can't // find anything that causes this bit to be set. throw new InvalidOperationException("Unsupported: non-default actor"); } } if (DefaultActor) { // Deep copy the current wireframe as the actor list // ----- // If a user wants to deal with subgroups to get a more // concise actor list, the data is all here. We're not // going to bother, though, because there are several // exceptions to account for in determining event actors. Actors = new List<Unit>(player.Wireframe.Count); foreach (var unit in player.Wireframe) { Actors.Add(new Unit(unit)); } } var targetType = bitReader.Read(2); if (targetType == 1) // Location target { var targetX = bitReader.Read(20); var targetY = bitReader.Read(20); var targetZ = bitReader.Read(32); TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ); } else if (targetType == 2) // Unit + Location target { TargetFlags = (int)bitReader.Read(8); WireframeIndex = (int)bitReader.Read(8); var unitId = (int)bitReader.Read(32); var unit = replay.GetUnitById(unitId); var unitTypeId = (int)bitReader.Read(16); if (unit == null) { var unitType = unitData.GetUnitType(unitTypeId); unit = new Unit(unitId, unitType); unit.typeId = unitTypeId; replay.GameUnits.Add(unitId, unit); } TargetUnit = unit; var targetHasPlayer = bitReader.Read(1) == 1; if (targetHasPlayer) { TargetPlayer = (int)bitReader.Read(4); } // 1.4.0 -- Don't really know what this was meant to fix if (replay.ReplayBuild >= 19595) { var targetHasTeam = bitReader.Read(1) == 1; if (targetHasTeam) { TargetTeam = (int)bitReader.Read(4); } } var targetX = bitReader.Read(20); var targetY = bitReader.Read(20); var targetZ = bitReader.Read(32); TargetLocation = Location.FromEventFormat(targetX, targetY, targetZ); } else if (targetType == 3) // Unit target { var id = bitReader.Read(32); // Again, if the user wants to determine exactly which // queue item is canceled in the case of a queue cancel // event (the most common case of this target specifier's // occurence), they can; however, it requires an additional // data structure that I don't want to bother with; however, // all the underlying data is available in the events list. TargetId = id; } var lastBit = bitReader.Read(1); // Should be 0; if not, misalignment is likely if (!AbilityFailed) { if (RightClick) { this.EventType = GameEventType.RightClick; } else { this.EventType = EventData.GetInstance().GetEventType(this.AbilityType); } } else { this.EventType = GameEventType.Inactive; } }
/// <summary> /// Copy constructor /// </summary> public Unit(Unit u) { this.id = u.id; this.type = u.type; this.typeId = u.typeId; }
/// <summary> /// Reads the 8 {16, 8, 8}, 8 {32} struct; the result is in AddedUnits / AddedUnitTypes. /// </summary> void HandleUnitArrays(BitReader bitReader, Replay replay, UnitData data) { int wireframeLength = 8; if (replay.ReplayBuild >= 22612) { wireframeLength = 9; // Maximum selection size has been increased to 500, up from 255. } var typesLength = (int)bitReader.Read(wireframeLength); AddedUnitTypes = new Dictionary<UnitType, int>(typesLength); // Guarantee order is maintained var subgroups = new List<KeyValuePair<UnitType, int>>(typesLength); for (int i = 0; i < typesLength; i++) { var unitTypeId = (int)bitReader.Read(16); var unitType = data.GetUnitType(unitTypeId); var unitSubtype = bitReader.Read(8); if (unitSubtype == 2) // hallucination -- cheers, Graylin { unitType = data.GetHallucination(unitType); } var unitCountType = (int)bitReader.Read(wireframeLength); if (unitType == UnitType.Unknown && AddedUnitTypes.ContainsKey(UnitType.Unknown)) { AddedUnitTypes[UnitType.Unknown] += unitCountType; } else { AddedUnitTypes.Add(unitType, unitCountType); } subgroups.Add(new KeyValuePair<UnitType, int>(unitType, unitCountType)); } var idsLength = (int)bitReader.Read(wireframeLength); AddedUnits = AddedUnits ?? new List<Unit>(idsLength); if (idsLength == 0) return; var subgroupsEnumerator = subgroups.GetEnumerator(); int currentSubgroupIndex; if (subgroupsEnumerator.MoveNext()) { currentSubgroupIndex = subgroupsEnumerator.Current.Value; } else return; for (int i = 0; i < idsLength; i++) { var unitId = (int)bitReader.Read(32); var unit = replay.GetUnitById(unitId); var unitType = subgroupsEnumerator.Current.Key; if (unit == null) { unit = new Unit(unitId, unitType); replay.GameUnits.Add(unitId, unit); } else { unit.UpdateType(unitType); } AddedUnits.Add(unit); if (--currentSubgroupIndex <= 0) { if (subgroupsEnumerator.MoveNext()) { currentSubgroupIndex = subgroupsEnumerator.Current.Value; } } } }