diff --git a/EOBot/Interpreter/BuiltInIdentifierConfigurator.cs b/EOBot/Interpreter/BuiltInIdentifierConfigurator.cs index 2c6ebd14d..c96701f1a 100644 --- a/EOBot/Interpreter/BuiltInIdentifierConfigurator.cs +++ b/EOBot/Interpreter/BuiltInIdentifierConfigurator.cs @@ -275,7 +275,7 @@ private void Chat(string chatText) var ms = DependencyMaster.TypeRegistry[_botIndex].Resolve(); var mapStateObj = new RuntimeEvaluatedMemberObjectVariable(); - mapStateObj.SymbolTable["characters"] = (true, () => new ArrayVariable(ms.Characters.Values.Select(GetMapStateCharacter).ToList())); + mapStateObj.SymbolTable["characters"] = (true, () => new ArrayVariable(ms.Characters.Select(GetMapStateCharacter).ToList())); mapStateObj.SymbolTable["npcs"] = (true, () => new ArrayVariable(ms.NPCs.Select(GetMapStateNPC).ToList())); mapStateObj.SymbolTable["items"] = (true, () => new ArrayVariable(ms.MapItems.Select(GetMapStateItem).ToList())); diff --git a/EOLib/Domain/Login/LoginActions.cs b/EOLib/Domain/Login/LoginActions.cs index 8b179a35d..e2126e352 100644 --- a/EOLib/Domain/Login/LoginActions.cs +++ b/EOLib/Domain/Login/LoginActions.cs @@ -183,8 +183,8 @@ public async Task CompleteCharacterLogin(int sessionID) _characterInventoryRepository.ItemInventory = new HashSet(data.CharacterItemInventory); _characterInventoryRepository.SpellInventory = new HashSet(data.CharacterSpellInventory); - _currentMapStateRepository.Characters = data.MapCharacters.Except(new[] { mainCharacter }).ToDictionary(k => k.ID, v => v); - _currentMapStateRepository.NPCs = new HashSet(data.MapNPCs); + _currentMapStateRepository.Characters = new MapEntityCollectionHashSet(c => c.ID, c => new MapCoordinate(c.X, c.Y), data.MapCharacters.Except(new[] { mainCharacter })); + _currentMapStateRepository.NPCs = new MapEntityCollectionHashSet(n => n.Index, n => new MapCoordinate(n.X, n.Y), data.MapNPCs); _currentMapStateRepository.MapItems = new MapEntityCollectionHashSet(item => item.UniqueID, item => new MapCoordinate(item.X, item.Y), data.MapItems); _playerInfoRepository.PlayerIsInGame = true; diff --git a/EOLib/Domain/Map/CurrentMapStateRepository.cs b/EOLib/Domain/Map/CurrentMapStateRepository.cs index 80873c577..af6528ef3 100644 --- a/EOLib/Domain/Map/CurrentMapStateRepository.cs +++ b/EOLib/Domain/Map/CurrentMapStateRepository.cs @@ -15,9 +15,9 @@ public interface ICurrentMapStateRepository bool IsJail { get; } - Dictionary Characters { get; set; } + MapEntityCollectionHashSet Characters { get; set; } - HashSet NPCs { get; set; } + MapEntityCollectionHashSet NPCs { get; set; } MapEntityCollectionHashSet MapItems { get; set; } @@ -50,9 +50,9 @@ public interface ICurrentMapStateProvider bool IsJail { get; } - IReadOnlyDictionary Characters { get; } + IReadOnlyMapEntityCollection Characters { get; } - IReadOnlyCollection NPCs { get; } + IReadOnlyMapEntityCollection NPCs { get; } IReadOnlyMapEntityCollection MapItems { get; } @@ -86,9 +86,9 @@ public class CurrentMapStateRepository : ICurrentMapStateRepository, ICurrentMap public bool IsJail => JailMapID == CurrentMapID; - public Dictionary Characters { get; set; } + public MapEntityCollectionHashSet Characters { get; set; } - public HashSet NPCs { get; set; } + public MapEntityCollectionHashSet NPCs { get; set; } public MapEntityCollectionHashSet MapItems { get; set; } @@ -110,9 +110,9 @@ public class CurrentMapStateRepository : ICurrentMapStateRepository, ICurrentMap public HashSet UnknownNPCIndexes { get; set; } - IReadOnlyDictionary ICurrentMapStateProvider.Characters => Characters; + IReadOnlyMapEntityCollection ICurrentMapStateProvider.Characters => Characters; - IReadOnlyCollection ICurrentMapStateProvider.NPCs => NPCs; + IReadOnlyMapEntityCollection ICurrentMapStateProvider.NPCs => NPCs; IReadOnlyMapEntityCollection ICurrentMapStateProvider.MapItems => MapItems; @@ -133,8 +133,8 @@ public void ResetState() ShowMiniMap = false; JailMapID = 0; - Characters = new Dictionary(); - NPCs = new HashSet(); + Characters = new MapEntityCollectionHashSet(x => x.ID, x => new MapCoordinate(x.X, x.Y)); + NPCs = new MapEntityCollectionHashSet(x => x.Index, x => new MapCoordinate(x.X, x.Y)); MapItems = new MapEntityCollectionHashSet(x => x.UniqueID, x => new MapCoordinate(x.X, x.Y)); OpenDoors = new HashSet(); PendingDoors = new HashSet(); diff --git a/EOLib/Domain/Map/MapCellStateProvider.cs b/EOLib/Domain/Map/MapCellStateProvider.cs index 320414576..4d11e22dd 100644 --- a/EOLib/Domain/Map/MapCellStateProvider.cs +++ b/EOLib/Domain/Map/MapCellStateProvider.cs @@ -39,11 +39,14 @@ public IMapCellState GetCellStateAt(int x, int y) var chest = CurrentMap.Chests.Where(c => c.X == x && c.Y == y && c.Key != ChestKey.None).Select(c => c.Key).FirstOrDefault(); var sign = CurrentMap.Signs.FirstOrDefault(s => s.X == x && s.Y == y); - var characters = _mapStateProvider.Characters.Values - .Concat(new[] { _characterProvider.MainCharacter }) - .Where(c => CharacterAtCoordinates(c, x, y)) - .ToList(); - var npc = _mapStateProvider.NPCs.FirstOrNone(n => NPCAtCoordinates(n, x, y)); + _mapStateProvider.Characters.TryGetValues(new MapCoordinate(x, y), out var characters); + if (_characterProvider.MainCharacter.X == x && _characterProvider.MainCharacter.Y == y) + characters.Add(_characterProvider.MainCharacter); + + Option npc = Option.None(); + if (_mapStateProvider.NPCs.TryGetValues(new MapCoordinate(x, y), out var npcs)) + npc = npcs.FirstOrNone(); + var items = _mapStateProvider.MapItems.TryGetValues(new MapCoordinate(x, y), out var mapItems) ? mapItems.OrderByDescending(i => i.UniqueID) : Enumerable.Empty(); @@ -58,25 +61,11 @@ public IMapCellState GetCellStateAt(int x, int y) ChestKey = chest.SomeNotNull(), Sign = sign.SomeNotNull().Map(s => new Sign(s)), Character = characters.FirstOrNone(), - Characters = characters, + Characters = characters.ToList(), NPC = npc }; } - private static bool CharacterAtCoordinates(Character.Character character, int x, int y) - { - return character.RenderProperties.IsActing(CharacterActionState.Walking) - ? character.RenderProperties.GetDestinationX() == x && character.RenderProperties.GetDestinationY() == y - : character.RenderProperties.MapX == x && character.RenderProperties.MapY == y; - } - - private static bool NPCAtCoordinates(NPC.NPC npc, int x, int y) - { - return npc.IsActing(NPCActionState.Walking) - ? npc.GetDestinationX() == x && npc.GetDestinationY() == y - : npc.X == x && npc.Y == y; - } - private IMapFile CurrentMap => _mapFileProvider.MapFiles[_mapStateProvider.CurrentMapID]; } diff --git a/EOLib/Domain/Map/MapEntityCollectionHashSet.cs b/EOLib/Domain/Map/MapEntityCollectionHashSet.cs index d57306409..3d92f45a7 100644 --- a/EOLib/Domain/Map/MapEntityCollectionHashSet.cs +++ b/EOLib/Domain/Map/MapEntityCollectionHashSet.cs @@ -56,9 +56,11 @@ public void Add(TValue value) _valueSet[hash] = value; } - public IEnumerator GetEnumerator() => _valueSet.Values.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Update(TValue oldValue, TValue newValue) + { + Remove(oldValue); + Add(newValue); + } public void Remove(TValue value) { @@ -74,6 +76,10 @@ public void Remove(TValue value) _valueSet.Remove(hash); } + public bool ContainsKey(int uniqueId) => _uniqueIdToHash.ContainsKey(uniqueId); + + public bool ContainsKey(MapCoordinate coordinate) => _mapCoordinateToHashList.ContainsKey(coordinate); + public bool TryGetValue(int uniqueId, out TValue value) { value = default; @@ -92,7 +98,7 @@ public bool TryGetValue(int uniqueId, out TValue value) public bool TryGetValues(MapCoordinate mapCoordinate, out HashSet values) { - values = default; + values = new HashSet(); if (!_mapCoordinateToHashList.ContainsKey(mapCoordinate)) return false; @@ -101,10 +107,14 @@ public bool TryGetValues(MapCoordinate mapCoordinate, out HashSet values if (!_valueSet.Any(x => hashes.Contains(x.Key))) return false; - values = new HashSet(_mapCoordinateToHashList[mapCoordinate].Select(x => _valueSet[x])); + values = this[mapCoordinate]; return true; } + + public IEnumerator GetEnumerator() => _valueSet.Values.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public interface IReadOnlyMapEntityCollection : IEnumerable @@ -112,6 +122,9 @@ public interface IReadOnlyMapEntityCollection : IEnumerable TValue this[int key1] { get; } HashSet this[MapCoordinate key2] { get; } + bool ContainsKey(int characterID); + bool ContainsKey(MapCoordinate mapCoordinate); + bool TryGetValue(int key1, out TValue value); bool TryGetValues(MapCoordinate key2, out HashSet values); } diff --git a/EOLib/PacketHandlers/AdminInteract/AdminInteractAgree.cs b/EOLib/PacketHandlers/AdminInteract/AdminInteractAgree.cs index bea6673bf..7a8bcf1df 100644 --- a/EOLib/PacketHandlers/AdminInteract/AdminInteractAgree.cs +++ b/EOLib/PacketHandlers/AdminInteract/AdminInteractAgree.cs @@ -36,12 +36,10 @@ public override bool HandlePacket(IPacket packet) _characterRepository.MainCharacter = Shown(_characterRepository.MainCharacter); else { - if (_currentMapStateRepository.Characters.ContainsKey(id)) + if (_currentMapStateRepository.Characters.TryGetValue(id, out var character)) { - var character = _currentMapStateRepository.Characters[id]; - var updatedCharacter = Shown(character); - _currentMapStateRepository.Characters[id] = updatedCharacter; + _currentMapStateRepository.Characters.Update(character, updatedCharacter); } else { diff --git a/EOLib/PacketHandlers/AdminInteract/AdminInteractRemove.cs b/EOLib/PacketHandlers/AdminInteract/AdminInteractRemove.cs index 9629e633f..ea6e52392 100644 --- a/EOLib/PacketHandlers/AdminInteract/AdminInteractRemove.cs +++ b/EOLib/PacketHandlers/AdminInteract/AdminInteractRemove.cs @@ -36,12 +36,10 @@ public override bool HandlePacket(IPacket packet) _characterRepository.MainCharacter = Hidden(_characterRepository.MainCharacter); else { - if (_currentMapStateRepository.Characters.ContainsKey(id)) + if (_currentMapStateRepository.Characters.TryGetValue(id, out var character)) { - var character = _currentMapStateRepository.Characters[id]; - var updatedCharacter = Hidden(character); - _currentMapStateRepository.Characters[id] = updatedCharacter; + _currentMapStateRepository.Characters.Update(character, updatedCharacter); } else { diff --git a/EOLib/PacketHandlers/Attack/AttackPlayerHandler.cs b/EOLib/PacketHandlers/Attack/AttackPlayerHandler.cs index b339ccdc0..d84aed677 100644 --- a/EOLib/PacketHandlers/Attack/AttackPlayerHandler.cs +++ b/EOLib/PacketHandlers/Attack/AttackPlayerHandler.cs @@ -35,13 +35,12 @@ public override bool HandlePacket(IPacket packet) var playerID = packet.ReadShort(); var direction = (EODirection)packet.ReadChar(); - if (_currentMapStateRepository.Characters.ContainsKey(playerID)) + if (_currentMapStateRepository.Characters.TryGetValue(playerID, out var character)) { - var character = _currentMapStateRepository.Characters[playerID]; if (character.RenderProperties.Direction != direction) { var renderProperties = character.RenderProperties.WithDirection(direction); - _currentMapStateRepository.Characters[playerID] = character.WithRenderProperties(renderProperties); + _currentMapStateRepository.Characters.Update(character, character.WithRenderProperties(renderProperties)); } foreach (var notifier in _otherCharacterAnimationNotifiers) diff --git a/EOLib/PacketHandlers/Avatar/AvatarAdminHandler.cs b/EOLib/PacketHandlers/Avatar/AvatarAdminHandler.cs index 53fdd853c..566bf01a7 100644 --- a/EOLib/PacketHandlers/Avatar/AvatarAdminHandler.cs +++ b/EOLib/PacketHandlers/Avatar/AvatarAdminHandler.cs @@ -49,11 +49,10 @@ public override bool HandlePacket(IPacket packet) var renderProps = _characterRepository.MainCharacter.RenderProperties.WithDirection(sourcePlayerDirection); _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithRenderProperties(renderProps); } - else if (_currentMapStateRepository.Characters.ContainsKey(sourcePlayerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(sourcePlayerId, out var sourceCharacter)) { - var character = _currentMapStateRepository.Characters[sourcePlayerId]; - var updatedCharacter = character.WithRenderProperties(character.RenderProperties.WithDirection(sourcePlayerDirection)); - _currentMapStateRepository.Characters[sourcePlayerId] = updatedCharacter; + var updatedCharacter = sourceCharacter.WithRenderProperties(sourceCharacter.RenderProperties.WithDirection(sourcePlayerDirection)); + _currentMapStateRepository.Characters.Update(sourceCharacter, updatedCharacter); } else { @@ -72,16 +71,14 @@ public override bool HandlePacket(IPacket packet) .WithStats(stats) .WithRenderProperties(renderProps); } - else if (_currentMapStateRepository.Characters.ContainsKey(targetPlayerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(targetPlayerId, out var targetCharacter)) { - var c = _currentMapStateRepository.Characters[targetPlayerId]; + var renderProps = targetCharacter.RenderProperties.WithIsDead(targetIsDead); - var renderProps = c.RenderProperties.WithIsDead(targetIsDead); - - var stats = c.Stats; + var stats = targetCharacter.Stats; stats = stats.WithNewStat(CharacterStat.HP, stats[CharacterStat.HP] - damage); - _currentMapStateRepository.Characters[targetPlayerId] = c.WithStats(stats).WithRenderProperties(renderProps); + _currentMapStateRepository.Characters.Update(targetCharacter, targetCharacter.WithStats(stats).WithRenderProperties(renderProps)); } else { diff --git a/EOLib/PacketHandlers/Avatar/AvatarAgreeHandler.cs b/EOLib/PacketHandlers/Avatar/AvatarAgreeHandler.cs index 3ced56148..2ecf05864 100644 --- a/EOLib/PacketHandlers/Avatar/AvatarAgreeHandler.cs +++ b/EOLib/PacketHandlers/Avatar/AvatarAgreeHandler.cs @@ -43,11 +43,7 @@ public override bool HandlePacket(IPacket packet) { currentCharacter = _characterRepository.MainCharacter; } - else if (_currentMapStateRepository.Characters.ContainsKey(playerID)) - { - currentCharacter = _currentMapStateRepository.Characters[playerID]; - } - else + else if (!_currentMapStateRepository.Characters.TryGetValue(playerID, out currentCharacter)) { _currentMapStateRepository.UnknownPlayerIDs.Add(playerID); return true; @@ -106,7 +102,7 @@ public override bool HandlePacket(IPacket packet) } else { - _currentMapStateRepository.Characters[playerID] = updatedCharacter; + _currentMapStateRepository.Characters.Update(currentCharacter, updatedCharacter); } return true; diff --git a/EOLib/PacketHandlers/Avatar/AvatarRemoveHandler.cs b/EOLib/PacketHandlers/Avatar/AvatarRemoveHandler.cs index 63fb31245..9cf2ba0ed 100644 --- a/EOLib/PacketHandlers/Avatar/AvatarRemoveHandler.cs +++ b/EOLib/PacketHandlers/Avatar/AvatarRemoveHandler.cs @@ -51,10 +51,9 @@ public override bool HandlePacket(IPacket packet) _characterRepository.HasAvatar = false; _currentMapStateRepository.VisibleSpikeTraps.Remove(_characterRepository.MainCharacter.RenderProperties.Coordinates()); } - else if (_currentMapStateRepository.Characters.ContainsKey(id)) + else if (_currentMapStateRepository.Characters.TryGetValue(id, out var character)) { - var character = _currentMapStateRepository.Characters[id]; - _currentMapStateRepository.Characters.Remove(id); + _currentMapStateRepository.Characters.Remove(character); _currentMapStateRepository.VisibleSpikeTraps.Remove(character.RenderProperties.Coordinates()); } else diff --git a/EOLib/PacketHandlers/Chat/PlayerChatByIDHandler.cs b/EOLib/PacketHandlers/Chat/PlayerChatByIDHandler.cs index 960df6a5e..339789e75 100644 --- a/EOLib/PacketHandlers/Chat/PlayerChatByIDHandler.cs +++ b/EOLib/PacketHandlers/Chat/PlayerChatByIDHandler.cs @@ -22,10 +22,10 @@ protected PlayerChatByIDHandler(ICurrentMapStateProvider currentMapStateProvider public override bool HandlePacket(IPacket packet) { var fromPlayerID = packet.ReadShort(); - if (!_currentMapStateProvider.Characters.ContainsKey(fromPlayerID)) + if (!_currentMapStateProvider.Characters.TryGetValue(fromPlayerID, out var character)) return false; - DoTalk(packet, _currentMapStateProvider.Characters[fromPlayerID]); + DoTalk(packet, character); return true; } diff --git a/EOLib/PacketHandlers/Effects/PlayerSpikeDamageHandler.cs b/EOLib/PacketHandlers/Effects/PlayerSpikeDamageHandler.cs index a7f9539cd..222d07d9a 100644 --- a/EOLib/PacketHandlers/Effects/PlayerSpikeDamageHandler.cs +++ b/EOLib/PacketHandlers/Effects/PlayerSpikeDamageHandler.cs @@ -35,10 +35,10 @@ public override bool HandlePacket(IPacket packet) var isDead = packet.ReadChar() != 0; var damageTaken = packet.ReadThree(); - if (_currentMapStateRepository.Characters.ContainsKey(characterId)) + if (_currentMapStateRepository.Characters.TryGetValue(characterId, out var character)) { - var updatedCharacter = _currentMapStateRepository.Characters[characterId].WithDamage(damageTaken, isDead); - _currentMapStateRepository.Characters[characterId] = updatedCharacter; + var updatedCharacter = character.WithDamage(damageTaken, isDead); + _currentMapStateRepository.Characters.Update(character, updatedCharacter); foreach (var notifier in _otherCharacterEventNotifiers) { diff --git a/EOLib/PacketHandlers/Face/FacePlayerHandler.cs b/EOLib/PacketHandlers/Face/FacePlayerHandler.cs index 234f0ffcb..7648745ed 100644 --- a/EOLib/PacketHandlers/Face/FacePlayerHandler.cs +++ b/EOLib/PacketHandlers/Face/FacePlayerHandler.cs @@ -31,14 +31,17 @@ public override bool HandlePacket(IPacket packet) var direction = (EODirection)packet.ReadChar(); if (!_mapStateRepository.Characters.ContainsKey(id)) - return false; + { + _mapStateRepository.UnknownPlayerIDs.Add(id); + return true; + } var character = _mapStateRepository.Characters[id]; var newRenderProps = character.RenderProperties.WithDirection(direction); var newCharacter = character.WithRenderProperties(newRenderProps); - _mapStateRepository.Characters[id] = newCharacter; + _mapStateRepository.Characters.Update(character, newCharacter); return true; } diff --git a/EOLib/PacketHandlers/Items/ItemGetHandler.cs b/EOLib/PacketHandlers/Items/ItemGetHandler.cs index 40f03bcc8..9f89e89c6 100644 --- a/EOLib/PacketHandlers/Items/ItemGetHandler.cs +++ b/EOLib/PacketHandlers/Items/ItemGetHandler.cs @@ -58,7 +58,8 @@ public override bool HandlePacket(IPacket packet) .WithNewStat(CharacterStat.MaxWeight, maxWeight); _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithStats(newStats); - _mapStateRepository.MapItems.Remove(_mapStateRepository.MapItems[uid]); + if (_mapStateRepository.MapItems.ContainsKey(uid)) + _mapStateRepository.MapItems.Remove(_mapStateRepository.MapItems[uid]); foreach (var notifier in _mainCharacterEventNotifiers) { diff --git a/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs b/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs index 66600914d..8b2bd562b 100644 --- a/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs +++ b/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs @@ -34,14 +34,12 @@ public override bool HandlePacket(IPacket packet) var instrument = packet.ReadChar(); var note = packet.ReadChar(); - if (_currentMapStateRepository.Characters.ContainsKey(playerId)) + if (_currentMapStateRepository.Characters.TryGetValue(playerId, out var character)) { - var c = _currentMapStateRepository.Characters[playerId]; - - if (c.RenderProperties.WeaponGraphic == instrument) + if (character.RenderProperties.WeaponGraphic == instrument) { - c = c.WithRenderProperties(c.RenderProperties.WithDirection(direction)); - _currentMapStateRepository.Characters[playerId] = c; + var updatedCharacter = character.WithRenderProperties(character.RenderProperties.WithDirection(direction)); + _currentMapStateRepository.Characters.Update(character, updatedCharacter); foreach (var notifier in _otherCharacterAnimationNotifiers) notifier.StartOtherCharacterAttackAnimation(playerId, note - 1); diff --git a/EOLib/PacketHandlers/MapInfo/MapInfoReplyHandler.cs b/EOLib/PacketHandlers/MapInfo/MapInfoReplyHandler.cs index 77f5f1fd0..ae4a85635 100644 --- a/EOLib/PacketHandlers/MapInfo/MapInfoReplyHandler.cs +++ b/EOLib/PacketHandlers/MapInfo/MapInfoReplyHandler.cs @@ -45,13 +45,13 @@ public override bool HandlePacket(IPacket packet) for (var i = 0; i < numberOfCharacters; i++) { var character = _characterFromPacketFactory.CreateCharacter(packet); - if (_currentMapStateRepository.Characters.ContainsKey(character.ID)) + if (_currentMapStateRepository.Characters.TryGetValue(character.ID, out var existingCharacter)) { - var existingCharacter = _currentMapStateRepository.Characters[character.ID]; var isRangedWeapon = _eifFileProvider.EIFFile.IsRangedWeapon(character.RenderProperties.WeaponGraphic); character = existingCharacter.WithAppliedData(character, isRangedWeapon); } - _currentMapStateRepository.Characters[character.ID] = character; + + _currentMapStateRepository.Characters.Update(existingCharacter, character); if (packet.ReadByte() != byte.MaxValue) throw new MalformedPacketException("Missing 255 byte after character data", packet); } @@ -60,8 +60,10 @@ public override bool HandlePacket(IPacket packet) while (packet.ReadPosition < packet.Length) { var npc = _npcFromPacketFactory.CreateNPC(packet); - _currentMapStateRepository.NPCs.RemoveWhere(n => n.Index == npc.Index); - _currentMapStateRepository.NPCs.Add(npc); + if (_currentMapStateRepository.NPCs.ContainsKey(npc.Index)) + _currentMapStateRepository.NPCs.Update(_currentMapStateRepository.NPCs[npc.Index], npc); + else + _currentMapStateRepository.NPCs.Add(npc); } return true; diff --git a/EOLib/PacketHandlers/NPC/NPCJunkHandler.cs b/EOLib/PacketHandlers/NPC/NPCJunkHandler.cs index ebdf5c03e..7b390736d 100644 --- a/EOLib/PacketHandlers/NPC/NPCJunkHandler.cs +++ b/EOLib/PacketHandlers/NPC/NPCJunkHandler.cs @@ -53,7 +53,8 @@ public override bool HandlePacket(IPacket packet) } } - _currentMapStateRepository.NPCs.RemoveWhere(npc => npc.ID == childNpcId); + foreach (var npc in _currentMapStateRepository.NPCs.Where(npc => npc.ID == childNpcId)) + _currentMapStateRepository.NPCs.Remove(npc); return true; } diff --git a/EOLib/PacketHandlers/NPC/NPCPlayerHandler.cs b/EOLib/PacketHandlers/NPC/NPCPlayerHandler.cs index 2e5cd5159..a8010cdd7 100644 --- a/EOLib/PacketHandlers/NPC/NPCPlayerHandler.cs +++ b/EOLib/PacketHandlers/NPC/NPCPlayerHandler.cs @@ -142,10 +142,10 @@ private void HandleNPCAttack(IPacket packet) foreach (var notifier in _mainCharacterNotifiers) notifier.NotifyTakeDamage(damageTaken, playerPercentHealth, isHeal: false); } - else if (_currentMapStateRepository.Characters.ContainsKey(characterID)) + else if (_currentMapStateRepository.Characters.TryGetValue(characterID, out var character)) { - var updatedCharacter = _currentMapStateRepository.Characters[characterID].WithDamage(damageTaken, isDead); - _currentMapStateRepository.Characters[characterID] = updatedCharacter; + var updatedCharacter = character.WithDamage(damageTaken, isDead); + _currentMapStateRepository.Characters.Update(character, updatedCharacter); foreach (var notifier in _otherCharacterNotifiers) notifier.OtherCharacterTakeDamage(characterID, playerPercentHealth, damageTaken, isHeal: false); diff --git a/EOLib/PacketHandlers/NPC/NPCSpecHandler.cs b/EOLib/PacketHandlers/NPC/NPCSpecHandler.cs index 8a9e12095..ca4586f1c 100644 --- a/EOLib/PacketHandlers/NPC/NPCSpecHandler.cs +++ b/EOLib/PacketHandlers/NPC/NPCSpecHandler.cs @@ -8,7 +8,6 @@ using Optional; using System; using System.Collections.Generic; -using System.Security.Cryptography; namespace EOLib.PacketHandlers.NPC { @@ -113,7 +112,8 @@ private void RemoveNPCFromView(int deadNPCIndex, int playerId, Option spell foreach (var notifier in _npcActionNotifiers) notifier.RemoveNPCFromView(deadNPCIndex, playerId, spellId, damage, showDeathAnimation); - _currentMapStateRepository.NPCs.RemoveWhere(npc => npc.Index == deadNPCIndex); + if (_currentMapStateRepository.NPCs.TryGetValue(deadNPCIndex, out var npc)) + _currentMapStateRepository.NPCs.Remove(npc); } private void UpdatePlayerDirection(int playerID, EODirection playerDirection) @@ -127,11 +127,11 @@ private void UpdatePlayerDirection(int playerID, EODirection playerDirection) _characterRepository.MainCharacter = updatedCharacter; } - else if (_currentMapStateRepository.Characters.ContainsKey(playerID)) + else if (_currentMapStateRepository.Characters.TryGetValue(playerID, out var character)) { - var updatedRenderProps = _currentMapStateRepository.Characters[playerID].RenderProperties.WithDirection(playerDirection); - var updatedCharacter = _currentMapStateRepository.Characters[playerID].WithRenderProperties(updatedRenderProps); - _currentMapStateRepository.Characters[playerID] = updatedCharacter; + var updatedRenderProps = character.RenderProperties.WithDirection(playerDirection); + var updatedCharacter = character.WithRenderProperties(updatedRenderProps); + _currentMapStateRepository.Characters.Update(character, updatedCharacter); } else { diff --git a/EOLib/PacketHandlers/NPC/NPCTakeDamageHandler.cs b/EOLib/PacketHandlers/NPC/NPCTakeDamageHandler.cs index 51809e80d..f616d5b3a 100644 --- a/EOLib/PacketHandlers/NPC/NPCTakeDamageHandler.cs +++ b/EOLib/PacketHandlers/NPC/NPCTakeDamageHandler.cs @@ -40,7 +40,7 @@ public override bool HandlePacket(IPacket packet) { var spellId = Family .SomeWhen(x => x == PacketFamily.Cast) - .Map(_ => packet.ReadShort()); + .Map(_ => packet.ReadShort()); var fromPlayerId = packet.ReadShort(); var fromDirection = (EODirection)packet.ReadChar(); @@ -67,11 +67,11 @@ public override bool HandlePacket(IPacket packet) _characterRepository.MainCharacter = character; } - else if (_currentMapStateRepository.Characters.ContainsKey(fromPlayerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(fromPlayerId, out var character)) { - var renderProps = _currentMapStateRepository.Characters[fromPlayerId].RenderProperties.WithDirection(fromDirection); - var updatedCharacter = _currentMapStateRepository.Characters[fromPlayerId].WithRenderProperties(renderProps); - _currentMapStateRepository.Characters[fromPlayerId] = updatedCharacter; + var renderProps = character.RenderProperties.WithDirection(fromDirection); + var updatedCharacter = character.WithRenderProperties(renderProps); + _currentMapStateRepository.Characters.Update(character, updatedCharacter); } else { @@ -81,10 +81,10 @@ public override bool HandlePacket(IPacket packet) // todo: this has the potential to bug out if the opponent ID is never reset and the player dies/leaves try { - var npc = _currentMapStateRepository.NPCs.Single(x => x.Index == npcIndex); + var npc = _currentMapStateRepository.NPCs[npcIndex]; var newNpc = npc.WithOpponentID(Option.Some(fromPlayerId)); - _currentMapStateRepository.NPCs.Remove(npc); - _currentMapStateRepository.NPCs.Add(newNpc); + _currentMapStateRepository.NPCs.Update(npc, newNpc); + foreach (var notifier in _npcNotifiers) notifier.NPCTakeDamage(npcIndex, fromPlayerId, damageToNpc, npcPctHealth, spellId); } diff --git a/EOLib/PacketHandlers/Paperdoll/ItemEquipHandler.cs b/EOLib/PacketHandlers/Paperdoll/ItemEquipHandler.cs index 7672cb26e..bec9f4992 100644 --- a/EOLib/PacketHandlers/Paperdoll/ItemEquipHandler.cs +++ b/EOLib/PacketHandlers/Paperdoll/ItemEquipHandler.cs @@ -71,8 +71,8 @@ protected bool HandlePaperdollPacket(IPacket packet, bool itemUnequipped) var update = _characterRepository.MainCharacter.ID == playerId ? Option.Some(_characterRepository.MainCharacter) - : _currentMapStateRepository.Characters.ContainsKey(playerId) - ? Option.Some(_currentMapStateRepository.Characters[playerId]) + : _currentMapStateRepository.Characters.TryGetValue(playerId, out var character) + ? Option.Some(character) : Option.None(); update.MatchSome(c => @@ -108,7 +108,7 @@ protected bool HandlePaperdollPacket(IPacket packet, bool itemUnequipped) } else { - _currentMapStateRepository.Characters[playerId] = c.WithStats(stats); + _currentMapStateRepository.Characters.Update(c, c.WithStats(stats)); } }); diff --git a/EOLib/PacketHandlers/Players/PlayersAgreeHandler.cs b/EOLib/PacketHandlers/Players/PlayersAgreeHandler.cs index 28a597ea8..b901c4867 100644 --- a/EOLib/PacketHandlers/Players/PlayersAgreeHandler.cs +++ b/EOLib/PacketHandlers/Players/PlayersAgreeHandler.cs @@ -73,15 +73,14 @@ public override bool HandlePacket(IPacket packet) _characterRepository.MainCharacter = existingCharacter.WithAppliedData(character, isRangedWeapon); _characterRepository.HasAvatar = true; } - else if (_mapStateRepository.Characters.ContainsKey(character.ID)) + else if (_mapStateRepository.Characters.TryGetValue(character.ID, out var existingCharacter)) { - var existingCharacter = _mapStateRepository.Characters[character.ID]; var isRangedWeapon = _eifFileProvider.EIFFile.IsRangedWeapon(character.RenderProperties.WeaponGraphic); - _mapStateRepository.Characters[character.ID] = existingCharacter.WithAppliedData(character, isRangedWeapon); + _mapStateRepository.Characters.Update(existingCharacter, existingCharacter.WithAppliedData(character, isRangedWeapon)); } else { - _mapStateRepository.Characters[character.ID] = character; + _mapStateRepository.Characters.Add(character); } return true; diff --git a/EOLib/PacketHandlers/Refresh/RefreshReplyHandler.cs b/EOLib/PacketHandlers/Refresh/RefreshReplyHandler.cs index bfd300763..e00bbf45e 100644 --- a/EOLib/PacketHandlers/Refresh/RefreshReplyHandler.cs +++ b/EOLib/PacketHandlers/Refresh/RefreshReplyHandler.cs @@ -46,18 +46,18 @@ public override bool HandlePacket(IPacket packet) { var data = _refreshReplyTranslator.TranslatePacket(packet); - var updatedMainCharacter = data.Characters.Single(IDMatches); + var updatedMainCharacter = data.Characters.Single(MainCharacterIDMatches); var updatedRenderProperties = _characterRepository.MainCharacter.RenderProperties .WithMapX(updatedMainCharacter.RenderProperties.MapX) .WithMapY(updatedMainCharacter.RenderProperties.MapY); - var withoutMainCharacter = data.Characters.Where(x => !IDMatches(x)); + var withoutMainCharacter = data.Characters.Where(x => !MainCharacterIDMatches(x)); _characterRepository.MainCharacter = _characterRepository.MainCharacter .WithRenderProperties(updatedRenderProperties); - _currentMapStateRepository.Characters = withoutMainCharacter.ToDictionary(k => k.ID, v => v); - _currentMapStateRepository.NPCs = new HashSet(data.NPCs); + _currentMapStateRepository.Characters = new MapEntityCollectionHashSet(c => c.ID, c => new MapCoordinate(c.X, c.Y), withoutMainCharacter); + _currentMapStateRepository.NPCs = new MapEntityCollectionHashSet(n => n.Index, n => new MapCoordinate(n.X, n.Y), data.NPCs); _currentMapStateRepository.MapItems = new MapEntityCollectionHashSet(item => item.UniqueID, item => new MapCoordinate(item.X, item.Y), data.Items); _currentMapStateRepository.OpenDoors.Clear(); @@ -72,7 +72,7 @@ public override bool HandlePacket(IPacket packet) return true; } - private bool IDMatches(Character x) + private bool MainCharacterIDMatches(Character x) { return x.ID == _characterRepository.MainCharacter.ID; } diff --git a/EOLib/PacketHandlers/Sit/PlayerSitHandlerBase.cs b/EOLib/PacketHandlers/Sit/PlayerSitHandlerBase.cs index 3620252fb..0d2595d29 100644 --- a/EOLib/PacketHandlers/Sit/PlayerSitHandlerBase.cs +++ b/EOLib/PacketHandlers/Sit/PlayerSitHandlerBase.cs @@ -47,21 +47,19 @@ public override bool HandlePacket(IPacket packet) .WithDirection(direction); _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithRenderProperties(updatedRenderProperties); } - else if (_currentMapStateRepository.Characters.ContainsKey(playerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(playerId, out var oldCharacter)) { - var oldCharacter = _currentMapStateRepository.Characters[playerId]; var renderProperties = oldCharacter.RenderProperties.WithSitState(sitState) .WithCurrentAction(sitState == SitState.Standing ? CharacterActionState.Standing : CharacterActionState.Sitting) .WithMapX(x) .WithMapY(y) .WithDirection(direction); - _currentMapStateRepository.Characters[playerId] = oldCharacter.WithRenderProperties(renderProperties); + _currentMapStateRepository.Characters.Update(oldCharacter, oldCharacter.WithRenderProperties(renderProperties)); } else { _currentMapStateRepository.UnknownPlayerIDs.Add(playerId); - return true; } return true; diff --git a/EOLib/PacketHandlers/Sit/PlayerStandHandlerBase.cs b/EOLib/PacketHandlers/Sit/PlayerStandHandlerBase.cs index bd22b8312..82d967f1f 100644 --- a/EOLib/PacketHandlers/Sit/PlayerStandHandlerBase.cs +++ b/EOLib/PacketHandlers/Sit/PlayerStandHandlerBase.cs @@ -40,15 +40,14 @@ public override bool HandlePacket(IPacket packet) .WithMapY(y); _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithRenderProperties(updatedRenderProperties); } - else if (_currentMapStateRepository.Characters.ContainsKey(playerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(playerId, out var oldCharacter)) { - var oldCharacter = _currentMapStateRepository.Characters[playerId]; var renderProperties = oldCharacter.RenderProperties.WithSitState(SitState.Standing) .WithCurrentAction(CharacterActionState.Standing) .WithMapX(x) .WithMapY(y); - _currentMapStateRepository.Characters[playerId] = oldCharacter.WithRenderProperties(renderProperties); + _currentMapStateRepository.Characters.Update(oldCharacter, oldCharacter.WithRenderProperties(renderProperties)); } else { diff --git a/EOLib/PacketHandlers/Spell/SpellTargetOtherHandler.cs b/EOLib/PacketHandlers/Spell/SpellTargetOtherHandler.cs index 512c3fc4e..e075837c5 100644 --- a/EOLib/PacketHandlers/Spell/SpellTargetOtherHandler.cs +++ b/EOLib/PacketHandlers/Spell/SpellTargetOtherHandler.cs @@ -47,11 +47,10 @@ public override bool HandlePacket(IPacket packet) var renderProps = _characterRepository.MainCharacter.RenderProperties.WithDirection(sourcePlayerDirection); _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithRenderProperties(renderProps); } - else if (_currentMapStateRepository.Characters.ContainsKey(sourcePlayerId)) + else if (_currentMapStateRepository.Characters.TryGetValue(sourcePlayerId, out var character)) { - var character = _currentMapStateRepository.Characters[sourcePlayerId]; var updatedCharacter = character.WithRenderProperties(character.RenderProperties.WithDirection(sourcePlayerDirection)); - _currentMapStateRepository.Characters[sourcePlayerId] = updatedCharacter; + _currentMapStateRepository.Characters.Update(character, updatedCharacter); } else { diff --git a/EOLib/PacketHandlers/Walk/WalkPlayerHandler.cs b/EOLib/PacketHandlers/Walk/WalkPlayerHandler.cs index 167893646..1d2004d14 100644 --- a/EOLib/PacketHandlers/Walk/WalkPlayerHandler.cs +++ b/EOLib/PacketHandlers/Walk/WalkPlayerHandler.cs @@ -36,20 +36,18 @@ public override bool HandlePacket(IPacket packet) { var characterID = packet.ReadShort(); - if (_currentMapStateRepository.Characters.ContainsKey(characterID)) + if (_currentMapStateRepository.Characters.TryGetValue(characterID, out var character)) { var dir = (EODirection)packet.ReadChar(); var x = packet.ReadChar(); var y = packet.ReadChar(); - var character = _currentMapStateRepository.Characters[characterID]; - // if character is walking, that means animator is handling position of character // if character is not walking (this is true in EOBot), update the domain model here if (!character.RenderProperties.IsActing(CharacterActionState.Walking)) { var renderProperties = EnsureCorrectXAndY(character.RenderProperties.WithDirection(dir), x, y); - _currentMapStateRepository.Characters[characterID] = character.WithRenderProperties(renderProperties); + _currentMapStateRepository.Characters.Update(character, character.WithRenderProperties(renderProperties)); } foreach (var notifier in _otherCharacterAnimationNotifiers) diff --git a/EOLib/PacketHandlers/Warp/WarpAgreeHandler.cs b/EOLib/PacketHandlers/Warp/WarpAgreeHandler.cs index 7512a17c2..adda26679 100644 --- a/EOLib/PacketHandlers/Warp/WarpAgreeHandler.cs +++ b/EOLib/PacketHandlers/Warp/WarpAgreeHandler.cs @@ -73,8 +73,8 @@ public override bool HandlePacket(IPacket packet) var withoutMainCharacter = warpAgreePacketData.Characters.Where(x => !MainCharacterIDMatches(x)); warpAgreePacketData = warpAgreePacketData.WithCharacters(withoutMainCharacter.ToList()); - _currentMapStateRepository.Characters = warpAgreePacketData.Characters.ToDictionary(k => k.ID, v => v); - _currentMapStateRepository.NPCs = new HashSet(warpAgreePacketData.NPCs); + _currentMapStateRepository.Characters = new MapEntityCollectionHashSet(c => c.ID, c => new MapCoordinate(c.X, c.Y), warpAgreePacketData.Characters); + _currentMapStateRepository.NPCs = new MapEntityCollectionHashSet(n => n.Index, n => new MapCoordinate(n.X, n.Y), warpAgreePacketData.NPCs); _currentMapStateRepository.MapItems = new MapEntityCollectionHashSet(item => item.UniqueID, item => new MapCoordinate(item.X, item.Y), warpAgreePacketData.Items); _currentMapStateRepository.OpenDoors.Clear(); _currentMapStateRepository.VisibleSpikeTraps.Clear(); diff --git a/EndlessClient/Controllers/MapInteractionController.cs b/EndlessClient/Controllers/MapInteractionController.cs index edb87c393..5145e5b79 100644 --- a/EndlessClient/Controllers/MapInteractionController.cs +++ b/EndlessClient/Controllers/MapInteractionController.cs @@ -254,9 +254,9 @@ private void HandlePickupResult(ItemPickupResult pickupResult, MapItem item) item.OwningPlayerID.MatchSome(playerId => { message = EOResourceID.STATUS_LABEL_ITEM_PICKUP_PROTECTED_BY; - if (_currentMapStateProvider.Characters.ContainsKey(playerId)) + if (_currentMapStateProvider.Characters.TryGetValue(playerId, out var character)) { - extra = $" {_currentMapStateProvider.Characters[playerId].Name}"; + extra = $" {character.Name}"; } }); diff --git a/EndlessClient/Network/UnknownEntitiesRequester.cs b/EndlessClient/Network/UnknownEntitiesRequester.cs index 1d9cbed34..4a66768d9 100644 --- a/EndlessClient/Network/UnknownEntitiesRequester.cs +++ b/EndlessClient/Network/UnknownEntitiesRequester.cs @@ -127,12 +127,12 @@ private void ClearOutOfRangeActors() { var mc = _characterProvider.MainCharacter; - var entities = _currentMapStateRepository.MapItems.Cast() + var entities = new List(_currentMapStateRepository.Characters) .Concat(_currentMapStateRepository.NPCs) - .Concat(_currentMapStateRepository.Characters.Values); + .Concat(_currentMapStateRepository.MapItems); - var seeDistanceUpper = (int)((_clientWindowSizeProvider.Height / 480.0) * UPPER_SEE_DISTANCE); - var seeDistanceLower = (int)((_clientWindowSizeProvider.Height / 480.0) * LOWER_SEE_DISTANCE); + var seeDistanceUpper = (int)(_clientWindowSizeProvider.Height / 480.0 * UPPER_SEE_DISTANCE); + var seeDistanceLower = (int)(_clientWindowSizeProvider.Height / 480.0 * LOWER_SEE_DISTANCE); var entitiesToRemove = new List(); foreach (var entity in entities) @@ -154,7 +154,7 @@ private void ClearOutOfRangeActors() foreach (var entity in entitiesToRemove) { if (entity is Character c) - _currentMapStateRepository.Characters.Remove(c.ID); + _currentMapStateRepository.Characters.Remove(c); else if (entity is NPC n) _currentMapStateRepository.NPCs.Remove(n); else if (entity is MapItem i) diff --git a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs index 38d9bc029..230411859 100644 --- a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs +++ b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs @@ -122,7 +122,7 @@ public void Emote(Emote whichEmote) public void StartOtherCharacterWalkAnimation(int characterID, MapCoordinate destination, EODirection direction) { - if (!_hudControlProvider.IsInGame || !_currentMapStateProvider.Characters.ContainsKey(characterID)) + if (!_hudControlProvider.IsInGame || !_currentMapStateProvider.Characters.TryGetValue(characterID, out var character)) return; Animator.StartOtherCharacterWalkAnimation(characterID, destination, direction); @@ -131,19 +131,19 @@ public void StartOtherCharacterWalkAnimation(int characterID, MapCoordinate dest _spikeTrapActions.HideSpikeTrap(characterID); _spikeTrapActions.ShowSpikeTrap(characterID); - if (IsSteppingStone(_currentMapStateProvider.Characters[characterID].RenderProperties)) + if (IsSteppingStone(character.RenderProperties)) _sfxPlayer.PlaySfx(SoundEffectID.JumpStone); } public void StartOtherCharacterAttackAnimation(int characterID, int noteIndex = -1) { - if (!_hudControlProvider.IsInGame || !_currentMapStateProvider.Characters.ContainsKey(characterID)) + if (!_hudControlProvider.IsInGame || !_currentMapStateProvider.Characters.TryGetValue(characterID, out var character)) return; - if (noteIndex >= 0 || IsInstrumentWeapon(_currentMapStateProvider.Characters[characterID].RenderProperties.WeaponGraphic)) + if (noteIndex >= 0 || IsInstrumentWeapon(character.RenderProperties.WeaponGraphic)) Animator.Emote(characterID, EOLib.Domain.Character.Emote.MusicNotes); - Animator.StartOtherCharacterAttackAnimation(characterID, () => PlayWeaponSound(_currentMapStateProvider.Characters[characterID], noteIndex)); + Animator.StartOtherCharacterAttackAnimation(characterID, () => PlayWeaponSound(character, noteIndex)); ShowWaterSplashiesIfNeeded(CharacterActionState.Attacking, characterID); } diff --git a/EndlessClient/Rendering/Character/CharacterAnimator.cs b/EndlessClient/Rendering/Character/CharacterAnimator.cs index ea5abbbe7..074e1bb8c 100644 --- a/EndlessClient/Rendering/Character/CharacterAnimator.cs +++ b/EndlessClient/Rendering/Character/CharacterAnimator.cs @@ -245,7 +245,7 @@ public bool Emote(int characterID, Emote whichEmote) else if (_currentMapStateRepository.Characters.TryGetValue(characterID, out var otherCharacter)) { var rp = otherCharacter.RenderProperties.WithEmote(whichEmote); - _currentMapStateRepository.Characters[characterID] = otherCharacter.WithRenderProperties(rp); + _currentMapStateRepository.Characters.Update(otherCharacter, otherCharacter.WithRenderProperties(rp)); } else { @@ -268,9 +268,11 @@ public void StopAllCharacterAnimations() _characterRepository.MainCharacter.WithRenderProperties( _characterRepository.MainCharacter.RenderProperties.ResetAnimationFrames()); - _currentMapStateRepository.Characters = _currentMapStateRepository.Characters.Values - .Select(c => c.WithRenderProperties(c.RenderProperties.ResetAnimationFrames())) - .ToDictionary(k => k.ID, v => v); + var characterPairs = _currentMapStateRepository.Characters + .Select(c => (Old: c, New: c.WithRenderProperties(c.RenderProperties.ResetAnimationFrames()))) + .ToList(); + foreach (var (Old, New) in characterPairs) + _currentMapStateRepository.Characters.Update(Old, New); } #region Walk Animation @@ -552,8 +554,8 @@ private void AnimateCharacterEmotes() { return pair.UniqueID == _characterRepository.MainCharacter.ID ? Option.Some(_characterRepository.MainCharacter) - : _currentMapStateRepository.Characters.ContainsKey(pair.UniqueID) - ? Option.Some(_currentMapStateRepository.Characters[pair.UniqueID]) + : _currentMapStateRepository.Characters.TryGetValue(pair.UniqueID, out var character) + ? Option.Some(character) : Option.None(); } @@ -566,7 +568,7 @@ private void UpdateCharacterInRepository(EOLib.Domain.Character.Character curren } else { - _currentMapStateRepository.Characters[nextFrameCharacter.ID] = nextFrameCharacter; + _currentMapStateRepository.Characters.Update(currentCharacter, nextFrameCharacter); } } @@ -575,7 +577,7 @@ private void ResetCharacterAnimationFrames(int characterID) var character = _currentMapStateRepository.Characters[characterID]; var renderProps = character.RenderProperties.ResetAnimationFrames(); var newCharacter = character.WithRenderProperties(renderProps); - _currentMapStateRepository.Characters[characterID] = newCharacter; + _currentMapStateRepository.Characters.Update(character, newCharacter); } } diff --git a/EndlessClient/Rendering/Character/CharacterRendererUpdater.cs b/EndlessClient/Rendering/Character/CharacterRendererUpdater.cs index 46703a6c6..405a077da 100644 --- a/EndlessClient/Rendering/Character/CharacterRendererUpdater.cs +++ b/EndlessClient/Rendering/Character/CharacterRendererUpdater.cs @@ -74,9 +74,9 @@ private void CreateMainCharacterRendererAndCacheProperties() private void CreateOtherCharacterRenderersAndCacheProperties() { - foreach (var id in _currentMapStateRepository.Characters.Keys) + foreach (var character in _currentMapStateRepository.Characters) { - var character = _currentMapStateRepository.Characters[id]; + var id = character.ID; _characterStateCache.HasCharacterWithID(id) .SomeWhen(b => b) @@ -146,9 +146,9 @@ private void RemoveStaleCharacters() private void UpdateDeadCharacters() { - var deadCharacters = new List(); + var deadCharacters = new List(); - foreach (var character in _currentMapStateRepository.Characters.Values.Where(x => x.RenderProperties.IsDead)) + foreach (var character in _currentMapStateRepository.Characters.Where(x => x.RenderProperties.IsDead)) { _characterStateCache.DeathStartTimes.SingleOrNone(x => x.UniqueID == character.ID) .Match( @@ -166,13 +166,13 @@ private void UpdateDeadCharacters() _characterRendererRepository.CharacterRenderers.Remove(character.ID); } - deadCharacters.Add(character.ID); + deadCharacters.Add(character); } }); } - foreach (var id in deadCharacters) - _currentMapStateRepository.Characters.Remove(id); + foreach (var dead in deadCharacters) + _currentMapStateRepository.Characters.Remove(dead); } private ICharacterRenderer InitializeRendererForCharacter(EOLib.Domain.Character.Character character) diff --git a/EndlessClient/Rendering/Map/ClickDispatcher.cs b/EndlessClient/Rendering/Map/ClickDispatcher.cs index 08301f4e3..7b86c0b3e 100644 --- a/EndlessClient/Rendering/Map/ClickDispatcher.cs +++ b/EndlessClient/Rendering/Map/ClickDispatcher.cs @@ -107,7 +107,7 @@ private bool CheckForMapItemClicks(IMapCellState cellState, MouseEventArgs event private bool CheckForEntityClicks(MouseEventArgs eventArgs) { - var entities = new List(_currentMapStateProvider.Characters.Select(x => x.Value)); + var entities = new List(_currentMapStateProvider.Characters); entities.Add(_characterProvider.MainCharacter); entities.AddRange(_currentMapStateProvider.NPCs); entities.AddRange(_currentMapProvider.CurrentMap.Signs); diff --git a/EndlessClient/Rendering/Map/DynamicMapObjectUpdater.cs b/EndlessClient/Rendering/Map/DynamicMapObjectUpdater.cs index 0a72628b3..825744af6 100644 --- a/EndlessClient/Rendering/Map/DynamicMapObjectUpdater.cs +++ b/EndlessClient/Rendering/Map/DynamicMapObjectUpdater.cs @@ -100,7 +100,7 @@ private void RemoveStaleSpikeTraps() foreach (var spikeTrap in _currentMapStateRepository.VisibleSpikeTraps) { - if (_currentMapStateRepository.Characters.Values + if (_currentMapStateRepository.Characters .Concat(new[] { _characterProvider.MainCharacter }) .Select(x => x.RenderProperties) .All(x => x.MapX != spikeTrap.X && x.MapY != spikeTrap.Y)) diff --git a/EndlessClient/Rendering/Map/MapChangedActions.cs b/EndlessClient/Rendering/Map/MapChangedActions.cs index c67ce197d..952fe36ff 100644 --- a/EndlessClient/Rendering/Map/MapChangedActions.cs +++ b/EndlessClient/Rendering/Map/MapChangedActions.cs @@ -189,7 +189,7 @@ private void ShowMapTransition(bool showMapTransition) private void AddSpikeTraps() { - foreach (var character in _currentMapStateRepository.Characters.Values) + foreach (var character in _currentMapStateRepository.Characters) { if (_currentMapProvider.CurrentMap.Tiles[character.RenderProperties.MapY, character.RenderProperties.MapX] == TileSpec.SpikesTrap) _currentMapStateRepository.VisibleSpikeTraps.Add(new MapCoordinate(character.RenderProperties.MapX, character.RenderProperties.MapY)); diff --git a/EndlessClient/Rendering/Map/MapEntityRendererProvider.cs b/EndlessClient/Rendering/Map/MapEntityRendererProvider.cs index b510f5cdd..8f53bdd11 100644 --- a/EndlessClient/Rendering/Map/MapEntityRendererProvider.cs +++ b/EndlessClient/Rendering/Map/MapEntityRendererProvider.cs @@ -28,8 +28,7 @@ public MapEntityRendererProvider(INativeGraphicsManager nativeGraphicsManager, IClientWindowSizeProvider clientWindowSizeProvider, IConfigurationProvider configurationProvider, ICharacterRendererProvider characterRendererProvider, - INPCRendererProvider npcRendererProvider, - ICharacterStateCache characterStateCache) + INPCRendererProvider npcRendererProvider) { GroundRenderer = new GroundLayerRenderer(nativeGraphicsManager, @@ -90,13 +89,14 @@ public MapEntityRendererProvider(INativeGraphicsManager nativeGraphicsManager, currentMapStateProvider), new OtherCharacterEntityRenderer(characterProvider, characterRendererProvider, - characterStateCache, + currentMapStateProvider, gridDrawCoordinateCalculator, clientWindowSizeProvider), new NPCEntityRenderer(characterProvider, gridDrawCoordinateCalculator, clientWindowSizeProvider, - npcRendererProvider), + npcRendererProvider, + currentMapStateProvider), new Overlay2LayerRenderer(nativeGraphicsManager, currentMapProvider, characterProvider, diff --git a/EndlessClient/Rendering/Map/MiniMapRenderer.cs b/EndlessClient/Rendering/Map/MiniMapRenderer.cs index d33fe3aa8..d83895804 100644 --- a/EndlessClient/Rendering/Map/MiniMapRenderer.cs +++ b/EndlessClient/Rendering/Map/MiniMapRenderer.cs @@ -110,7 +110,7 @@ protected override void OnDrawControl(GameTime gameTime) _spriteBatch.Draw(_miniMapTarget, baseTargetDrawLoc, Color.White); var entities = new IMapEntity[] { _characterProvider.MainCharacter } - .Concat(_currentMapStateProvider.Characters.Values) + .Concat(_currentMapStateProvider.Characters) .Concat(_currentMapStateProvider.NPCs); foreach (var entity in entities) diff --git a/EndlessClient/Rendering/Map/SpikeTrapActions.cs b/EndlessClient/Rendering/Map/SpikeTrapActions.cs index 3b94a6260..a15a16864 100644 --- a/EndlessClient/Rendering/Map/SpikeTrapActions.cs +++ b/EndlessClient/Rendering/Map/SpikeTrapActions.cs @@ -38,13 +38,10 @@ public void ShowSpikeTrap(MapCoordinate coordinate, bool isMainCharacter = false public void ShowSpikeTrap(int characterId) { - if (!_currentMapStateRepository.Characters.ContainsKey(characterId)) + if (!_currentMapStateRepository.Characters.TryGetValue(characterId, out var character)) return; - var character = _currentMapStateRepository.Characters[characterId]; - ShowSpikeTrap(new MapCoordinate( - character.RenderProperties.GetDestinationX(), - character.RenderProperties.GetDestinationY())); + ShowSpikeTrap(new MapCoordinate(character.X, character.Y)); } public void HideSpikeTrap(MapCoordinate coordinate) @@ -54,13 +51,10 @@ public void HideSpikeTrap(MapCoordinate coordinate) public void HideSpikeTrap(int characterId) { - if (!_currentMapStateRepository.Characters.ContainsKey(characterId)) + if (!_currentMapStateRepository.Characters.TryGetValue(characterId, out var character)) return; - var character = _currentMapStateRepository.Characters[characterId]; - HideSpikeTrap(new MapCoordinate( - character.RenderProperties.MapX, - character.RenderProperties.MapY)); + HideSpikeTrap(new MapCoordinate(character.X, character.Y)); } } diff --git a/EndlessClient/Rendering/MapEntityRenderers/NPCEntityRenderer.cs b/EndlessClient/Rendering/MapEntityRenderers/NPCEntityRenderer.cs index f8f5b37e0..5070d25d0 100644 --- a/EndlessClient/Rendering/MapEntityRenderers/NPCEntityRenderer.cs +++ b/EndlessClient/Rendering/MapEntityRenderers/NPCEntityRenderer.cs @@ -1,11 +1,9 @@ using EndlessClient.Rendering.Map; using EndlessClient.Rendering.NPC; using EOLib.Domain.Character; -using EOLib.Domain.Extensions; -using EOLib.Domain.NPC; +using EOLib.Domain.Map; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using System; using System.Linq; namespace EndlessClient.Rendering.MapEntityRenderers @@ -13,14 +11,17 @@ namespace EndlessClient.Rendering.MapEntityRenderers public class NPCEntityRenderer : BaseMapEntityRenderer { private readonly INPCRendererProvider _npcRendererProvider; + private readonly ICurrentMapStateProvider _currentMapStateProvider; public NPCEntityRenderer(ICharacterProvider characterProvider, IGridDrawCoordinateCalculator gridDrawCoordinateCalculator, IClientWindowSizeProvider clientWindowSizeProvider, - INPCRendererProvider npcRendererProvider) + INPCRendererProvider npcRendererProvider, + ICurrentMapStateProvider currentMapStateProvider) : base(characterProvider, gridDrawCoordinateCalculator, clientWindowSizeProvider) { _npcRendererProvider = npcRendererProvider; + _currentMapStateProvider = currentMapStateProvider; } public override MapRenderLayer RenderLayer => MapRenderLayer.Npc; @@ -29,34 +30,21 @@ public NPCEntityRenderer(ICharacterProvider characterProvider, protected override bool ElementExistsAt(int row, int col) { - return _npcRendererProvider.NPCRenderers.Values.Any(IsNpcAt); - bool IsNpcAt(INPCRenderer rend) => NPCEntityRenderer.IsNpcAt(rend.NPC, row, col); + return _currentMapStateProvider.NPCs.ContainsKey(new MapCoordinate(col, row)); } public override void RenderElementAt(SpriteBatch spriteBatch, int row, int col, int alpha, Vector2 additionalOffset = default) { - var indicesToRender = _npcRendererProvider.NPCRenderers.Values - .Where(IsNpcAt) - .Select(n => n.NPC.Index); + var indicesToRender = _currentMapStateProvider.NPCs[new MapCoordinate(col, row)].Select(n => n.Index); foreach (var index in indicesToRender) { if (!_npcRendererProvider.NPCRenderers.ContainsKey(index) || _npcRendererProvider.NPCRenderers[index] == null) - throw new InvalidOperationException( - $"NPC renderer for ID {index} is null or missing! Did you call MapRenderer.Update() before calling MapRenderer.Draw()?"); + return; - var renderer = _npcRendererProvider.NPCRenderers[index]; - renderer.DrawToSpriteBatch(spriteBatch); + _npcRendererProvider.NPCRenderers[index].DrawToSpriteBatch(spriteBatch); } - - bool IsNpcAt(INPCRenderer rend) => NPCEntityRenderer.IsNpcAt(rend.NPC, row, col); - } - - private static bool IsNpcAt(EOLib.Domain.NPC.NPC npc, int row, int col) - { - return (npc.IsActing(NPCActionState.Walking) && npc.GetDestinationX() == col && npc.GetDestinationY() == row) || - (npc.X == col && npc.Y == row); } } } diff --git a/EndlessClient/Rendering/MapEntityRenderers/OtherCharacterEntityRenderer.cs b/EndlessClient/Rendering/MapEntityRenderers/OtherCharacterEntityRenderer.cs index e27c6aaf9..25dd1a64e 100644 --- a/EndlessClient/Rendering/MapEntityRenderers/OtherCharacterEntityRenderer.cs +++ b/EndlessClient/Rendering/MapEntityRenderers/OtherCharacterEntityRenderer.cs @@ -1,6 +1,7 @@ using EndlessClient.Rendering.Character; using EndlessClient.Rendering.Map; using EOLib.Domain.Character; +using EOLib.Domain.Map; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Linq; @@ -10,17 +11,17 @@ namespace EndlessClient.Rendering.MapEntityRenderers public class OtherCharacterEntityRenderer : BaseMapEntityRenderer { private readonly ICharacterRendererProvider _characterRendererProvider; - private readonly ICharacterStateCache _characterStateCache; + private readonly ICurrentMapStateProvider _currentMapStateProvider; public OtherCharacterEntityRenderer(ICharacterProvider characterProvider, ICharacterRendererProvider characterRendererProvider, - ICharacterStateCache characterStateCache, + ICurrentMapStateProvider currentMapStateProvider, IGridDrawCoordinateCalculator gridDrawCoordinateCalculator, IClientWindowSizeProvider clientWindowSizeProvider) : base(characterProvider, gridDrawCoordinateCalculator, clientWindowSizeProvider) { _characterRendererProvider = characterRendererProvider; - _characterStateCache = characterStateCache; + _currentMapStateProvider = currentMapStateProvider; } public override MapRenderLayer RenderLayer => MapRenderLayer.OtherCharacters; @@ -29,33 +30,21 @@ public OtherCharacterEntityRenderer(ICharacterProvider characterProvider, protected override bool ElementExistsAt(int row, int col) { - return _characterStateCache.OtherCharacters.Values - .Any(IsCharAt); - - bool IsCharAt(EOLib.Domain.Character.Character c) => OtherCharacterEntityRenderer.IsCharAt(c, row, col); + return _currentMapStateProvider.Characters.ContainsKey(new MapCoordinate(col, row)); } public override void RenderElementAt(SpriteBatch spriteBatch, int row, int col, int alpha, Vector2 additionalOffset = default) { - var toRender = _characterStateCache.OtherCharacters.Values.Where(IsCharAt); + var toRender = _currentMapStateProvider.Characters[new MapCoordinate(col, row)]; - foreach (var rend in toRender) + foreach (var id in toRender.Select(x => x.ID)) { - var id = rend.ID; - if (!_characterRendererProvider.CharacterRenderers.ContainsKey(id) || _characterRendererProvider.CharacterRenderers[id] == null) return; - var renderer = _characterRendererProvider.CharacterRenderers[id]; - renderer.DrawToSpriteBatch(spriteBatch); + _characterRendererProvider.CharacterRenderers[id].DrawToSpriteBatch(spriteBatch); } - - bool IsCharAt(EOLib.Domain.Character.Character c) => OtherCharacterEntityRenderer.IsCharAt(c, row, col); - } - private static bool IsCharAt(EOLib.Domain.Character.Character c, int row, int col) - { - return row == c.Y && col == c.X; } } } \ No newline at end of file