From 844c443a43e5015e4dcebb7d457ea9670fbb1783 Mon Sep 17 00:00:00 2001 From: Talic Date: Wed, 17 May 2023 13:18:22 +0300 Subject: [PATCH] refactoring, entity rework --- SotnApi/ActorApiTests/SpawnActorShould.cs | 2 +- SotnApi/SotnApi/AlucardApi.cs | 11 +- .../Constants/Addresses/Alucard/Entity.cs | 2 + .../Constants/Addresses/Alucard/Stats.cs | 1 + .../Constants/Addresses/Alucard/Timers.cs | 6 +- SotnApi/SotnApi/Constants/Addresses/Game.cs | 5 +- .../Constants/Addresses/ZoneTransitions.cs | 120 ++--- .../Constants/Values/Alucard/Equipment.cs | 33 ++ .../SotnApi/Constants/Values/Game/Entities.cs | 69 +-- .../Constants/Values/Game/Enums/Stage.cs | 85 ++++ .../Constants/Values/Game/Enums/Zone.cs | 50 -- .../SotnApi/Constants/Values/Game/Status.cs | 2 +- .../SotnApi/Constants/Values/Game/Various.cs | 91 ++-- SotnApi/SotnApi/EntityApi.cs | 72 ++- SotnApi/SotnApi/GameApi.cs | 156 +++---- SotnApi/SotnApi/Interfaces/IAlucardApi.cs | 1 + SotnApi/SotnApi/Interfaces/IEntityApi.cs | 10 +- SotnApi/SotnApi/Interfaces/IGameApi.cs | 6 +- SotnApi/SotnApi/Models/Entity.cs | 430 +++++++++++++----- SotnApi/SotnApi/Models/LiveEntity.cs | 255 ----------- SotnApi/SotnApi/Models/SearchableActor.cs | 2 +- SotnApi/SotnApi/Models/TeleportZone.cs | 2 +- SotnApi/SotnApi/SotnApi.csproj | 7 +- SotnApi/SotnApi/SotnApi.xml | 24 +- 24 files changed, 743 insertions(+), 699 deletions(-) create mode 100644 SotnApi/SotnApi/Constants/Values/Game/Enums/Stage.cs delete mode 100644 SotnApi/SotnApi/Constants/Values/Game/Enums/Zone.cs delete mode 100644 SotnApi/SotnApi/Models/LiveEntity.cs diff --git a/SotnApi/ActorApiTests/SpawnActorShould.cs b/SotnApi/ActorApiTests/SpawnActorShould.cs index 04eb10c..a7e8dc3 100644 --- a/SotnApi/ActorApiTests/SpawnActorShould.cs +++ b/SotnApi/ActorApiTests/SpawnActorShould.cs @@ -19,7 +19,7 @@ public void CallsWriteByteRangeMethodOfMemAPI_ExactlyOnce() //Act classUnderTest.SpawnEntity(blankActor); //Assert - mockedMemAPI.Received(1).WriteByteRange(Arg.Any(), blankActor.Value); + mockedMemAPI.Received(1).WriteByteRange(Arg.Any(), blankActor.Data); } } } diff --git a/SotnApi/SotnApi/AlucardApi.cs b/SotnApi/SotnApi/AlucardApi.cs index 76ad0ad..410e5e9 100644 --- a/SotnApi/SotnApi/AlucardApi.cs +++ b/SotnApi/SotnApi/AlucardApi.cs @@ -1188,7 +1188,6 @@ public uint StunTimer memAPI.WriteU16(Timers.Stun, value); } } - public uint ContactDamage { get @@ -1321,7 +1320,7 @@ public void TakeOneItemByName(string name) public bool IsInvincible() { - return memAPI.ReadByte(Timers.Invincibility) > 0 || memAPI.ReadByte(Timers.KnockbackInvincibility) > 0 || memAPI.ReadByte(Timers.PotionInvincibility) > 0 || memAPI.ReadByte(Timers.FreezeInvincibility) > 0; + return memAPI.ReadU16(Timers.Invincibility) > 0 || memAPI.ReadByte(Timers.KnockbackInvincibility) > 0 || memAPI.ReadByte(Timers.PotionInvincibility) > 0 || memAPI.ReadByte(Timers.FreezeInvincibility) > 0; } public bool HasControl() @@ -1332,7 +1331,7 @@ public bool HasControl() public bool HasHitbox() { - return memAPI.ReadByte(Entity.Address + Entities.HitboxWidthOffset) > 0 && memAPI.ReadByte(Entity.Address + Entities.HitboxHeightOffset) > 0; + return memAPI.ReadByte(Entity.Address + Entities.HitboxWidth) > 0 && memAPI.ReadByte(Entity.Address + Entities.HitboxHeight) > 0; } public void Heal(uint amount) @@ -1341,6 +1340,12 @@ public void Heal(uint amount) memAPI.WriteByte(Effects.HealTrigger, 1); } + public void InstantDeath() + { + memAPI.WriteS16(Constants.Addresses.Alucard.Entity.Step, 0x10); + memAPI.WriteS16(Constants.Addresses.Alucard.Entity.Step2, 0); + } + public void ActivateStopwatch() { memAPI.WriteByte(Effects.Activator, SotnApi.Constants.Values.Alucard.Effects.Stopwatch); diff --git a/SotnApi/SotnApi/Constants/Addresses/Alucard/Entity.cs b/SotnApi/SotnApi/Constants/Addresses/Alucard/Entity.cs index a98d630..2d6e391 100644 --- a/SotnApi/SotnApi/Constants/Addresses/Alucard/Entity.cs +++ b/SotnApi/SotnApi/Constants/Addresses/Alucard/Entity.cs @@ -9,5 +9,7 @@ public static class Entity public const long AttackHitboxHeight_2 = 0x07409B; public const long HorizontalVelocityWhole = 0x0733E2; public const long HorizontalVelocityFract = 0x0733E1; + public const long Step = 0x073404; + public const long Step2 = 0x073406; } } diff --git a/SotnApi/SotnApi/Constants/Addresses/Alucard/Stats.cs b/SotnApi/SotnApi/Constants/Addresses/Alucard/Stats.cs index b48228d..b54830b 100644 --- a/SotnApi/SotnApi/Constants/Addresses/Alucard/Stats.cs +++ b/SotnApi/SotnApi/Constants/Addresses/Alucard/Stats.cs @@ -35,5 +35,6 @@ public static class Stats public const long State = 0x073404; public const long Action = 0x073406; public const long ContactDamage = 0x073418; + public const long ArmorResistance = 0x097C28; } } diff --git a/SotnApi/SotnApi/Constants/Addresses/Alucard/Timers.cs b/SotnApi/SotnApi/Constants/Addresses/Alucard/Timers.cs index 4dff852..14dc48f 100644 --- a/SotnApi/SotnApi/Constants/Addresses/Alucard/Timers.cs +++ b/SotnApi/SotnApi/Constants/Addresses/Alucard/Timers.cs @@ -2,16 +2,16 @@ { public static class Timers { - public const long Stun = 0x072EFC; //Size: 2 Byte + public const long Stun = 0x072EFC; //Size: 4 Byte public const long Poison = 0x072F00; //Size: 2 Byte public const long Curse = 0x072F03; //Size: 2 Byte public const long Shine = 0x072F09; //Size: 2 Byte - public const long VisualEffectTimer = 0x072F04; + public const long VisualEffectTimer = 0x072F04; //Size: 2 Byte public const long DarkMetamorphasis = 0x072F17; public const long AttackPotion = 0x13982D; public const long StrengthPotion = 0x139838; public const long DefencePotion = 0x139829; - public const long Invincibility = 0x072F1B; + public const long Invincibility = 0x072F1A; public const long PotionInvincibility = 0x072F1C; public const long KnockbackInvincibility = 0x13B5E8; public const long FreezeInvincibility = 0x097420; diff --git a/SotnApi/SotnApi/Constants/Addresses/Game.cs b/SotnApi/SotnApi/Constants/Addresses/Game.cs index d26f36e..596e30a 100644 --- a/SotnApi/SotnApi/Constants/Addresses/Game.cs +++ b/SotnApi/SotnApi/Constants/Addresses/Game.cs @@ -84,7 +84,7 @@ public static class Game public const long MapYPos = 0x0730B4; public const long Room = 0x1375BC; public const long Area = 0x1375BD; - public const long Zone2 = 0x0974A0; + public const long StageId = 0x0974A0; public const long Hours = 0x097C30; public const long Minutes = 0x097C34; public const long Seconds = 0x097C38; @@ -106,7 +106,8 @@ public static class Game public const long CanWarp = 0x03C710; public const long MapItemsCollectedStart = 0x03BEE0; public const long UnderwaterPhysics = 0x097448; - public const long MusicTrack = 0x138458; + public const long MusicTrack = 0x13901C; + public const long LastLoadedMusicTrack = 0x138458; public const long MusicVolume = 0x13B668; public const long TrackVolume = 0x139820; public const long VolumeSetInstruction = 0x136280; diff --git a/SotnApi/SotnApi/Constants/Addresses/ZoneTransitions.cs b/SotnApi/SotnApi/Constants/Addresses/ZoneTransitions.cs index 186b3bd..351d899 100644 --- a/SotnApi/SotnApi/Constants/Addresses/ZoneTransitions.cs +++ b/SotnApi/SotnApi/Constants/Addresses/ZoneTransitions.cs @@ -5,93 +5,93 @@ namespace SotnApi.Constants.Addresses { public static class ZoneTransitionAddresses { - public static readonly Dictionary CastleEntrance = new Dictionary + public static readonly Dictionary CastleEntrance = new Dictionary { - { Zone.MarbleGallery, 0x0A268C }, - { Zone.UndergroundCaverns, 0x0A2696 }, - { Zone.AlchemyLaboratory, 0x0A26A0 }, - { Zone.Warp, 0x0A26AA }, + { Stage.MarbleGallery, 0x0A268C }, + { Stage.UndergroundCaverns, 0x0A2696 }, + { Stage.AlchemyLaboratory, 0x0A26A0 }, + { Stage.Warp, 0x0A26AA }, }; - public static readonly Dictionary AlchemyLaboratory = new Dictionary + public static readonly Dictionary AlchemyLaboratory = new Dictionary { - { Zone.CastleEntrance, 0x0A252E }, - { Zone.MarbleGallery, 0x0A2538 }, - { Zone.RoyalChapel, 0x0A2542 }, + { Stage.CastleEntrance, 0x0A252E }, + { Stage.MarbleGallery, 0x0A2538 }, + { Stage.RoyalChapel, 0x0A2542 }, }; - public static readonly Dictionary Colosseum = new Dictionary + public static readonly Dictionary Colosseum = new Dictionary { - {Zone.RoyalChapel, 0x0A2664}, - {Zone.OlroxsQuarters, 0x0A266E}, + {Stage.RoyalChapel, 0x0A2664}, + {Stage.OlroxsQuarters, 0x0A266E}, }; - public static readonly Dictionary OlroxsQuarters = new Dictionary + public static readonly Dictionary OlroxsQuarters = new Dictionary { - { Zone.MarbleGallery, 0x0A254C }, - { Zone.RoyalChapel, 0x0A2556 }, - { Zone.Colosseum, 0x0A2560 }, - { Zone.Warp, 0x0A256A }, + { Stage.MarbleGallery, 0x0A254C }, + { Stage.RoyalChapel, 0x0A2556 }, + { Stage.Colosseum, 0x0A2560 }, + { Stage.Warp, 0x0A256A }, }; - public static readonly Dictionary RoyalChapel = new Dictionary + public static readonly Dictionary RoyalChapel = new Dictionary { - { Zone.OlroxsQuarters, 0x0A25A6 }, - { Zone.Colosseum, 0x0A25B0 }, - { Zone.AlchemyLaboratory, 0x0A25BA }, - { Zone.CastleKeep, 0x0A25C4 }, + { Stage.OlroxsQuarters, 0x0A25A6 }, + { Stage.Colosseum, 0x0A25B0 }, + { Stage.AlchemyLaboratory, 0x0A25BA }, + { Stage.CastleKeep, 0x0A25C4 }, }; - public static readonly Dictionary Warp = new Dictionary + public static readonly Dictionary Warp = new Dictionary { - { Zone.CastleKeep, 0x0A2574 }, - { Zone.OuterWall, 0x0A257E }, - { Zone.OlroxsQuarters, 0x0A2588 }, - { Zone.CastleEntrance, 0x0A2592 }, - { Zone.AbandonedMine, 0x0A259C }, + { Stage.CastleKeep, 0x0A2574 }, + { Stage.OuterWall, 0x0A257E }, + { Stage.OlroxsQuarters, 0x0A2588 }, + { Stage.CastleEntrance, 0x0A2592 }, + { Stage.AbandonedMine, 0x0A259C }, }; - public static readonly Dictionary MarbleGallery = new Dictionary + public static readonly Dictionary MarbleGallery = new Dictionary { - { Zone.AlchemyLaboratory, 0x0A245C }, - { Zone.OlroxsQuarters, 0x0A2466 }, - { Zone.OuterWall, 0x0A2470 }, - { Zone.CastleEntrance, 0x0A247A }, - { Zone.UndergroundCaverns, 0x0A2484 }, - { Zone.CenterCube, 0x0A248E }, + { Stage.AlchemyLaboratory, 0x0A245C }, + { Stage.OlroxsQuarters, 0x0A2466 }, + { Stage.OuterWall, 0x0A2470 }, + { Stage.CastleEntrance, 0x0A247A }, + { Stage.UndergroundCaverns, 0x0A2484 }, + { Stage.CenterCube, 0x0A248E }, }; - public static readonly Dictionary LongLibrary = new Dictionary + public static readonly Dictionary LongLibrary = new Dictionary { - { Zone.OuterWall, 0x0A2498 }, + { Stage.OuterWall, 0x0A2498 }, }; - public static readonly Dictionary ClockTower = new Dictionary + public static readonly Dictionary ClockTower = new Dictionary { - { Zone.CastleKeep, 0x0A24B6 }, - { Zone.OuterWall, 0x0A24C0 }, + { Stage.CastleKeep, 0x0A24B6 }, + { Stage.OuterWall, 0x0A24C0 }, }; - public static readonly Dictionary CastleKeep = new Dictionary + public static readonly Dictionary CastleKeep = new Dictionary { - { Zone.ClockTower, 0x0A261E }, - { Zone.Warp, 0x0A2628 }, - { Zone.RoyalChapel, 0x0A2632 }, + { Stage.ClockTower, 0x0A261E }, + { Stage.Warp, 0x0A2628 }, + { Stage.RoyalChapel, 0x0A2632 }, }; - public static readonly Dictionary UndergroundCaverns = new Dictionary + public static readonly Dictionary UndergroundCaverns = new Dictionary { - { Zone.AbandonedMine, 0x0A2650 }, - { Zone.MarbleGallery, 0x0A2646 }, - { Zone.Nightmare, 0x0A265A }, - { Zone.CastleEntrance, 0x0A263C }, + { Stage.AbandonedMine, 0x0A2650 }, + { Stage.MarbleGallery, 0x0A2646 }, + { Stage.Nightmare, 0x0A265A }, + { Stage.CastleEntrance, 0x0A263C }, }; - public static readonly Dictionary AbandonedMine = new Dictionary + public static readonly Dictionary AbandonedMine = new Dictionary { - { Zone.UndergroundCaverns, 0x0A251A }, - { Zone.Warp, 0x0A2524 }, - { Zone.Catacombs, 0x0A2510 }, + { Stage.UndergroundCaverns, 0x0A251A }, + { Stage.Warp, 0x0A2524 }, + { Stage.Catacombs, 0x0A2510 }, }; - public static readonly Dictionary Catacombs = new Dictionary + public static readonly Dictionary Catacombs = new Dictionary { - { Zone.AbandonedMine, 0x0A24FC }, + { Stage.AbandonedMine, 0x0A24FC }, }; - public static readonly Dictionary OuterWall = new Dictionary + public static readonly Dictionary OuterWall = new Dictionary { - { Zone.MarbleGallery, 0x0A25EC }, - { Zone.LongLibrary, 0x0A25F6 }, - { Zone.Warp, 0x0A2600 }, - { Zone.ClockTower, 0x0A260A }, + { Stage.MarbleGallery, 0x0A25EC }, + { Stage.LongLibrary, 0x0A25F6 }, + { Stage.Warp, 0x0A2600 }, + { Stage.ClockTower, 0x0A260A }, }; } } diff --git a/SotnApi/SotnApi/Constants/Values/Alucard/Equipment.cs b/SotnApi/SotnApi/Constants/Values/Alucard/Equipment.cs index 2c39d02..ba6c5ac 100644 --- a/SotnApi/SotnApi/Constants/Values/Alucard/Equipment.cs +++ b/SotnApi/SotnApi/Constants/Values/Alucard/Equipment.cs @@ -266,6 +266,39 @@ public static class Equipment "Secret boots", "Alucart mail" }; + public static readonly List Relics = new List + { + "Soul of Bat", + "Fire of Bat", + "Echo of Bat", + "Force of Echo", + "Soul of Wolf", + "Power of Wolf", + "Skill of Wolf", + "Form of Mist", + "Power of Mist", + "Gas Cloud", + "Cube of Zoe", + "Spirit Orb", + "Gravity Boots", + "Leap Stone", + "Holy Symbol", + "Faerie Scroll", + "Jewel of Open", + "Merman Statue", + "Bat Card", + "Ghost Card", + "Faerie Card", + "Demon Card", + "Sword Card", + "Sprite Card", + "Nose Devil Card", + "Heart of Vlad", + "Tooth of Vlad", + "Rib of Vlad", + "Ring of Vlad", + "Eye of Vlad", + }; public const uint HelmStart = 0x1A; public const uint CloakStart = 0x30; public const uint AccessoryStart = 0x39; diff --git a/SotnApi/SotnApi/Constants/Values/Game/Entities.cs b/SotnApi/SotnApi/Constants/Values/Game/Entities.cs index 105a3ff..e7114ef 100644 --- a/SotnApi/SotnApi/Constants/Values/Game/Entities.cs +++ b/SotnApi/SotnApi/Constants/Values/Game/Entities.cs @@ -4,44 +4,49 @@ public static class Entities { public const int EnemyEntitiesCount = 131; public const int FriendEntitiesCount = 6; - public const int Size = 146; public const int Poison = 0x81; - public const int Curse = 1; - public const int Stone = 2; - public const int LockedOn = 2; + public const int Curse = 0x1; + public const int Stone = 0x2; + public const int LockedOn = 0x2; public const int Slam = 0x25; - public const int Offset = 0xBC; + public const int Size = 0xBC; - public const int XposOffset = 0x02; - public const int YposOffset = 0x06; + public const int Xpos = 0x00; + public const int Ypos = 0x06; public const int AccelerationX = 0x08; public const int AccelerationY = 0x0C; - public const int SpeedFractOffset = 0x09; - public const int SpeedWholeOffset = 0xA; - public const int SpeedVerticalFractOffset = 0xD; - public const int SpeedVerticalWholeOffset = 0xE; - public const int SpeedVerticalNegativeOffset = 0xF; - public const int FacingOffset = 0x14; - public const int PaletteOffset = 0x16; - public const int ColorModeOffset = 0x18; - public const int AiIdOffset = 0x28; - public const int LockOnOffset = 0x2C; - public const int ItemIndexOffset = 0x30; //RelicIndex - public const int EnemyNameIndex = 0x3A; - public const int DefOffset = 0x3A; - public const int HitboxAutoToggleOffset = 0x3C; - public const int HpOffset = 0x3E; - public const int DamageOffset = 0x40; - public const int DamageTypeAOffset = 0x42; - public const int DamageTypeBOffset = 0x43; - public const int HitboxWidthOffset = 0x46; - public const int HitboxHeightOffset = 0x47; - public const int InvincibilityFramesOffset = 0x49; - public const int AnimationFrameIndexOffset = 0x50; - public const int AnimationFrameDurationOffset = 0x52; - public const int AnimationSetOffset = 0x54; - public const int AnimationFrameOffset = 0x58; + public const int Facing = 0x14; + //public const int Unk = 0x16; display mode? + public const int Palette = 0x17; + public const int BlendMode = 0x18; + public const int ZPriority = 0x24; + public const int ObjectId = 0x26; + public const int UpdateFunctionAddress = 0x28; + public const int Step = 0x2C; + public const int Step2 = 0x2E; + public const int SubId = 0x30; // Item/Relic Index + public const int ObjectRoomIndex = 0x32; + public const int Flags = 0x34; + public const int Unk38 = 0x38; + public const int EnemyIndex = 0x3A; // determines display name and XP and in some cases Defense + public const int Defense = 0x3A; + public const int HitboxAutoToggle = 0x3C; + public const int Hp = 0x3E; + public const int Damage = 0x40; + public const int DamageTypeA = 0x42; + public const int DamageTypeB = 0x43; + public const int HitboxWidth = 0x46; + public const int HitboxHeight = 0x47; + public const int InvincibilityFrames = 0x49; + public const int AnimationFrameIndex = 0x50; + public const int AnimationFrameDuration = 0x52; + public const int AnimationSet = 0x54; + public const int AnimationCurrentFrame = 0x58; + //* 0x64 */ s32 firstPolygonIndex; + /// + /// Spawning entities in these can cause relics not to spawn. + /// public static readonly long[] ReservedSlots = { 0x076e98, diff --git a/SotnApi/SotnApi/Constants/Values/Game/Enums/Stage.cs b/SotnApi/SotnApi/Constants/Values/Game/Enums/Stage.cs new file mode 100644 index 0000000..0e820e9 --- /dev/null +++ b/SotnApi/SotnApi/Constants/Values/Game/Enums/Stage.cs @@ -0,0 +1,85 @@ +namespace SotnApi.Constants.Values.Game.Enums +{ + public enum Stage + { + MarbleGallery = 0, + OuterWall = 1, + LongLibrary = 2, + Catacombs = 3, + OlroxsQuarters = 4, + AbandonedMine = 5, + RoyalChapel = 6, + CastleEntrance = 7, //second visit + CenterCube = 8, + UndergroundCaverns = 9, + Colosseum = 0xA, + CastleKeep = 0xB, + AlchemyLaboratory = 0xC, + ClockTower = 0xD, + Warp = 0xE, + OuterWall2 = 0xF, + MarbleGallery2 = 10, + Nightmare = 0x12, + AlchemyLaboratory2 = 0x13, //Slogra & Gaibon + ClockTower2 = 0x14, //Karasuman + LongLibrary2 = 0x15, //Lesser Demon + Cerberus = 0x16, + ClockRoomCutscene = 0x17, //Maria + RichterFight = 0x18, + Hippogryph = 0x19, + Doppleganger10 = 0x1A, + Scylla = 0x1B, + MinotaurWerewolf = 0x1C, + Granfaloon = 0x1D, + Olrox = 0x1E, + Prologue = 0x1F, + BlackMarbleGallery = 0x20, + ReverseOuterWall = 0x21, + ForbiddenLibrary = 0x22, + FloatingCatacombs = 0x23, + DeathWingsLair = 0x24, + Cave = 0x25, //Inverted Mines + AntiChapel = 0x26, + ReverseEntrance = 0x27, + ReverseCenterCube = 0x28, + ReverseCaverns = 0x29, + ReverseColosseum = 0x2A, + ReverseCastleKeep = 0x2B, + NecromancyLaboratory = 0x2C, + ReverseClockTower = 0x2D, + ReverseWarp = 0x2E, + OuterWall3 = 0x2F, //ThirdOuterWall + OuterWall4 = 0x30, + OuterWall5 = 0x31, + OuterWall6 = 0x32, + OuterWall7 = 0x33, + OuterWall8 = 0x34, + ReverseClockTower2 = 0x35, // + Galamoth = 0x36, + Akmodan = 0x37, + ShaftDracula = 0x38, + Doppleganger40 = 0x39, + Creature = 0x3A, + Mesusa = 0x3B, + Death = 0x3C, + Beelzebub = 0x3D, + Trio = 0x3E, + OuterWall9 = 0x3F, + Debug = 0x40, + CastleEntrance1 = 0x41, //first visit + RoyalChapel2 = 0x42, + LongLibrary3 = 0x43, + OuterWall10 = 0x44, + TitleScreen = 0x45, + Test1 = 0x46, + Test2 = 0x47, + Test3 = 0x48, + Test4 = 0x49, + Test5 = 0x4A, + CastleKeep3 = 0x4B, + Test22 = 0x4C, + Test23 = 0x4D, + Test24 = 0x4E, + Test25 = 0x4F, + } +} diff --git a/SotnApi/SotnApi/Constants/Values/Game/Enums/Zone.cs b/SotnApi/SotnApi/Constants/Values/Game/Enums/Zone.cs deleted file mode 100644 index 8bcee2d..0000000 --- a/SotnApi/SotnApi/Constants/Values/Game/Enums/Zone.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace SotnApi.Constants.Values.Game.Enums -{ - public enum Zone - { - MarbleGallery = 0, - OuterWall = 1, - LongLibrary = 2, - Catacombs = 3, - OlroxsQuarters = 4, - AbandonedMine = 5, - RoyalChapel = 6, - CastleEntrance = 7, - CenterCube = 8, - UndergroundCaverns = 9, - Colosseum = 0xA, - CastleKeep = 0xB, - AlchemyLaboratory = 0xC, - ClockTower = 0xD, - Warp = 0xE, - Nightmare = 0x12, - SlograGaibon = 0x13, - Karasuman = 0x14, - LesserDemon = 0x15, - Cerberus = 0x16, - ClockRoomCutscene = 0x17, - RichterFight = 0x18, - Hippogryph = 0x19, - Doppleganger10 = 0x1A, - Scylla = 0x1B, - MinotaurWerewolf = 0x1C, - Granfaloon = 0x1D, - Olrox = 0x1E, - Prologue = 0x1F, - BlackMarbleGallery = 0x20, - ReverseOuterWall = 0x21, - ForbiddenLibrary = 0x22, - FloatingCatacombs = 0x23, - DeathWingsLair = 0x24, - Cave = 0x25, //Inverted Mines - AntiChapel = 0x26, - ReverseEntrance = 0x27, - ReverseCaverns = 0x29, - ReverseColosseum = 0x2A, - ReverseCastleKeep = 0x2B, - NecromancyLaboratory = 0x2C, - ReverseClockTower = 0x2D, - ReverseWarp = 0x2E, - //ThirdOuterWall = 0x2F, - } -} diff --git a/SotnApi/SotnApi/Constants/Values/Game/Status.cs b/SotnApi/SotnApi/Constants/Values/Game/Status.cs index da013c0..860d40a 100644 --- a/SotnApi/SotnApi/Constants/Values/Game/Status.cs +++ b/SotnApi/SotnApi/Constants/Values/Game/Status.cs @@ -5,7 +5,7 @@ public static class Status public const uint InGame = 0x2; public const uint MainMenu = 0x8; public const uint MenuOpen = 0x1; - public const uint Loading = 0x880A0400; + public const uint Loading = 0x88; public const uint Transition = 0x3; } } diff --git a/SotnApi/SotnApi/Constants/Values/Game/Various.cs b/SotnApi/SotnApi/Constants/Values/Game/Various.cs index c91db1a..033f61f 100644 --- a/SotnApi/SotnApi/Constants/Values/Game/Various.cs +++ b/SotnApi/SotnApi/Constants/Values/Game/Various.cs @@ -1,5 +1,4 @@ using SotnApi.Constants.Values.Game.Enums; -using SotnApi.Models; using System.Collections.Generic; namespace SotnApi.Constants.Values.Game @@ -18,12 +17,12 @@ public static class Various public const uint CanWarp = 0x0E; public const int MapItemsCollectedSize = 143; public const int UnderwaterPhysicsOn = 0x0090; - public const uint DefaultVolumeSetInstruction = 0x84A5B668; //LH $a1 -0x4998($a1) - public const uint MuteVolumeSetInstruction = 0x34050000; //MOVE $a1 0x0000 - public const uint DefaultMovementSpeedDirectionInstruction = 0x14620002; //BNE v1 v0 0x2 - public const uint FlippedMovementSpeedDirectionInstruction = 0x10620002; //BEQ v1 v0 0x2 - public const uint MonoStartingStereoSettingInstruction = 0x34020001; //MOVE v2 0x01, Default - public const uint StereoStartingStereoSettingInstruction = 0x34020000; //MOVE v2 0x00 + public const uint DefaultVolumeSetInstruction = 0x84A5B668; //LH $a1 -0x4998($a1) + public const uint MuteVolumeSetInstruction = 0x34050000; //MOVE $a1 0x0000 + public const uint DefaultMovementSpeedDirectionInstruction = 0x14620002; //BNE v1 v0 0x2 + public const uint FlippedMovementSpeedDirectionInstruction = 0x10620002; //BEQ v1 v0 0x2 + public const uint MonoStartingStereoSettingInstruction = 0x34020001; //MOVE v2 0x01 + public const uint StereoStartingStereoSettingInstruction = 0x34020000; //MOVE v2 0x00 public static readonly Dictionary CharacterMap = new Dictionary { {0x43, ','}, @@ -220,49 +219,49 @@ public static class Various "Shaft", "Dracula" }; - public static readonly List SafeLibraryCardZones = new List() + public static readonly List SafeLibraryCardZones = new List() { - new TeleportZone { Zone = (ushort)Zone.MarbleGallery, Xpos = 125, Ypos = 125, Room = 40}, - new TeleportZone { Zone = (ushort)Zone.OuterWall, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.LongLibrary, Xpos = 132, Ypos = 16, Room = 40}, - new TeleportZone { Zone = (ushort)Zone.Catacombs, Xpos = 125, Ypos = 125, Room = 40}, - new TeleportZone { Zone = (ushort)Zone.OlroxsQuarters, Xpos = 125, Ypos = 125, Room = 40}, - new TeleportZone { Zone = (ushort)Zone.AbandonedMine, Xpos = 125, Ypos = 125, Room = 24}, - new TeleportZone { Zone = (ushort)Zone.RoyalChapel, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.CastleEntrance, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.CenterCube, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.UndergroundCaverns, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.Colosseum, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.CastleKeep, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.AlchemyLaboratory, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.MarbleGallery, Xpos = 125, Ypos = 125, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.OuterWall, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.LongLibrary, Xpos = 132, Ypos = 16, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.Catacombs, Xpos = 125, Ypos = 125, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.OlroxsQuarters, Xpos = 125, Ypos = 125, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.AbandonedMine, Xpos = 125, Ypos = 125, Room = 24}, + new Models.TeleportDestination { Zone = (ushort)Stage.RoyalChapel, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.CastleEntrance, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.CenterCube, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.UndergroundCaverns, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.Colosseum, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.CastleKeep, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.AlchemyLaboratory, Xpos = 125, Ypos = 125, Room = 48}, //new TeleportZone { Zone = (ushort)Zone.ClockTower, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.Warp, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.Nightmare, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.SlograGaibon, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Karasuman, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.LesserDemon, Xpos = 500, Ypos = 500, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Cerberus, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Hippogryph, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Doppleganger10, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Scylla, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.MinotaurWerewolf, Xpos = 255, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Granfaloon, Xpos = 225, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.Olrox, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.BlackMarbleGallery, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.ReverseOuterWall, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.ForbiddenLibrary, Xpos = 125, Ypos = 125, Room = 8}, - new TeleportZone { Zone = (ushort)Zone.FloatingCatacombs, Xpos = 125, Ypos = 125, Room = 32}, - new TeleportZone { Zone = (ushort)Zone.DeathWingsLair, Xpos = 125, Ypos = 125, Room = 16}, - new TeleportZone { Zone = (ushort)Zone.Cave, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.AntiChapel, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.ReverseEntrance, Xpos = 125, Ypos = 125, Room = 16}, - new TeleportZone { Zone = (ushort)Zone.ReverseCaverns, Xpos = 125, Ypos = 125, Room = 0}, - new TeleportZone { Zone = (ushort)Zone.ReverseColosseum, Xpos = 125, Ypos = 125, Room = 8}, - new TeleportZone { Zone = (ushort)Zone.ReverseCastleKeep, Xpos = 125, Ypos = 125, Room = 40}, - new TeleportZone { Zone = (ushort)Zone.NecromancyLaboratory, Xpos = 125, Ypos = 125, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.Warp, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.Nightmare, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.AlchemyLaboratory2, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.ClockTower2, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.LongLibrary2, Xpos = 500, Ypos = 500, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Cerberus, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Hippogryph, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Doppleganger10, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Scylla, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.MinotaurWerewolf, Xpos = 255, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Granfaloon, Xpos = 225, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.Olrox, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.BlackMarbleGallery, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseOuterWall, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.ForbiddenLibrary, Xpos = 125, Ypos = 125, Room = 8}, + new Models.TeleportDestination { Zone = (ushort)Stage.FloatingCatacombs, Xpos = 125, Ypos = 125, Room = 32}, + new Models.TeleportDestination { Zone = (ushort)Stage.DeathWingsLair, Xpos = 125, Ypos = 125, Room = 16}, + new Models.TeleportDestination { Zone = (ushort)Stage.Cave, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.AntiChapel, Xpos = 125, Ypos = 125, Room = 48}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseEntrance, Xpos = 125, Ypos = 125, Room = 16}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseCaverns, Xpos = 125, Ypos = 125, Room = 0}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseColosseum, Xpos = 125, Ypos = 125, Room = 8}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseCastleKeep, Xpos = 125, Ypos = 125, Room = 40}, + new Models.TeleportDestination { Zone = (ushort)Stage.NecromancyLaboratory, Xpos = 125, Ypos = 125, Room = 40}, //new TeleportZone { Zone = (ushort)Zone.ReverseClockTower, Xpos = 125, Ypos = 125, Room = 0}, //Darkwing //new TeleportZone { Zone = (ushort)Zone.ReverseClockTower, Xpos = 125, Ypos = 125, Room = 48}, - new TeleportZone { Zone = (ushort)Zone.ReverseWarp, Xpos = 125, Ypos = 125, Room = 8}, + new Models.TeleportDestination { Zone = (ushort)Stage.ReverseWarp, Xpos = 125, Ypos = 125, Room = 8}, }; } } diff --git a/SotnApi/SotnApi/EntityApi.cs b/SotnApi/SotnApi/EntityApi.cs index 1ea0632..6f57d04 100644 --- a/SotnApi/SotnApi/EntityApi.cs +++ b/SotnApi/SotnApi/EntityApi.cs @@ -23,10 +23,10 @@ public long FindEnemyEntity(int minHp, int maxHp, int[]? bannedHpValues = null) if (minHp < 1) { throw new ArgumentOutOfRangeException(nameof(minHp), "minHp must be greater than 0"); } if (maxHp < 1) { throw new ArgumentOutOfRangeException(nameof(maxHp), "maxHp must be greater than 0"); } - long start = Game.EnemyEntitiesStart; + long address = Game.EnemyEntitiesStart; for (int i = 0; i < Entities.EnemyEntitiesCount; i++) { - LiveEntity actor = GetLiveEntity(start); + Entity actor = GetLiveEntity(address); bool notBanned = true; if (bannedHpValues is not null) @@ -43,9 +43,9 @@ public long FindEnemyEntity(int minHp, int maxHp, int[]? bannedHpValues = null) if (actor.HitboxHeight > 1 && actor.HitboxWidth > 1 && actor.Hp >= minHp && actor.Hp <= maxHp && actor.Damage > 0 && notBanned) { - return start; + return address; } - start += Entities.Offset; + address += Entities.Size; } return 0; @@ -56,16 +56,16 @@ public long FindEnemyEntity(int minHp, int maxHp, List bannedAc if (minHp < 1) { throw new ArgumentOutOfRangeException(nameof(minHp), "minHp must be greater than 0"); } if (maxHp < 1) { throw new ArgumentOutOfRangeException(nameof(maxHp), "maxHp must be greater than 0"); } - long start = Game.EnemyEntitiesStart; + long address = Game.EnemyEntitiesStart; for (int i = 0; i < Entities.EnemyEntitiesCount; i++) { - LiveEntity actor = GetLiveEntity(start); + Entity actor = GetLiveEntity(address); bool notBanned = true; for (int j = 0; j < bannedActors.Count; j++) { if (((bannedActors[j].Damage > 0 && actor.Damage == bannedActors[j].Damage) || bannedActors[j].Damage == 0) && - actor.AiId == bannedActors[j].AiId) + actor.UpdateFunctionAddress == bannedActors[j].UpdateFunctionAddress) { notBanned = false; break; @@ -74,9 +74,9 @@ public long FindEnemyEntity(int minHp, int maxHp, List bannedAc if (actor.HitboxWidth > 1 && actor.HitboxHeight > 1 && actor.Hp >= minHp && actor.Hp <= maxHp && actor.Damage > 0 && notBanned) { - return start; + return address; } - start += Entities.Offset; + address += Entities.Size; } return 0; @@ -86,12 +86,12 @@ public long FindEntityFrom(List actors, bool enemy = true) { if (actors.Count < 1) { throw new ArgumentOutOfRangeException(nameof(actors), "actors count must be greater than 0"); } - long start = enemy ? Game.EnemyEntitiesStart : Game.FriendlyEntitiesStart; + long address = enemy ? Game.EnemyEntitiesStart : Game.FriendlyEntitiesStart; int count = enemy ? Entities.EnemyEntitiesCount : Entities.FriendEntitiesCount; for (int i = 0; i < count; i++) { - LiveEntity currentActor = GetLiveEntity(start); + Entity currentActor = GetLiveEntity(address); bool match = false; for (int j = 0; j < actors.Count; j++) @@ -103,7 +103,7 @@ public long FindEntityFrom(List actors, bool enemy = true) ((actor.Ypos > 0 && currentActor.Ypos == actor.Ypos) || actor.Ypos == 0) && ((actor.Damage > 0 && currentActor.Damage == actor.Damage) || actor.Damage == 0) && ((actor.Hp > 0 && currentActor.Hp == actor.Hp) || actor.Hp == 0) && - currentActor.AiId == actor.AiId) + currentActor.UpdateFunctionAddress == actor.UpdateFunctionAddress) { match = true; break; @@ -112,46 +112,46 @@ public long FindEntityFrom(List actors, bool enemy = true) if (match) { - return start; + return address; } else { - start += Entities.Offset; + address += Entities.Size; } } return 0; } - public List GetAllActors() + public List GetAllEntities() { List ActorAddresses = new(); long start = Game.EnemyEntitiesStart; for (int i = 0; i < Entities.EnemyEntitiesCount; i++) { - long hitboxWidth = memAPI.ReadByte(start + Entities.HitboxWidthOffset); - long hitboxHeight = memAPI.ReadByte(start + Entities.HitboxHeightOffset); - long hp = memAPI.ReadU16(start + Entities.HpOffset); - long damage = memAPI.ReadU16(start + Entities.DamageOffset); + long hitboxWidth = memAPI.ReadByte(start + Entities.HitboxWidth); + long hitboxHeight = memAPI.ReadByte(start + Entities.HitboxHeight); + long hp = memAPI.ReadU16(start + Entities.Hp); + long damage = memAPI.ReadU16(start + Entities.Damage); long sprite = memAPI.ReadU16(start + 22); if (hitboxWidth > 0 || hitboxHeight > 0 || hp > 0 || damage > 0 || sprite > 0) { ActorAddresses.Add(start); } - start += Entities.Offset; + start += Entities.Size; } return ActorAddresses; } - public List GetAllActors(List actors) + public List GetAllEntities(List actors) { List ActorAddresses = new(); - long start = Game.EnemyEntitiesStart; + long address = Game.EnemyEntitiesStart; for (int i = 0; i < Entities.EnemyEntitiesCount; i++) { - LiveEntity currentActor = GetLiveEntity(start); + Entity currentActor = GetLiveEntity(address); for (int j = 0; j < actors.Count; j++) { @@ -160,14 +160,14 @@ public List GetAllActors(List actors) ((actor.HitboxHeight > 0 && currentActor.HitboxHeight == actor.HitboxHeight) || currentActor.HitboxHeight > 1) && ((actor.Xpos > 0 && currentActor.Xpos == actor.Xpos) || actor.Xpos == 0) && ((actor.Ypos > 0 && currentActor.Ypos == actor.Ypos) || actor.Ypos == 0) && - currentActor.Hp == actor.Hp && currentActor.Damage == actor.Damage && currentActor.AiId == actor.AiId) + currentActor.Hp == actor.Hp && currentActor.Damage == actor.Damage && currentActor.UpdateFunctionAddress == actor.UpdateFunctionAddress) { - ActorAddresses.Add(start); + ActorAddresses.Add(address); break; } } - start += Entities.Offset; + address += Entities.Size; } return ActorAddresses; @@ -178,9 +178,9 @@ public List GetEntity(long address) return memAPI.ReadByteRange(address, Entities.Size); } - public LiveEntity GetLiveEntity(long address) + public Entity GetLiveEntity(long address) { - return new LiveEntity(address, memAPI); + return new Entity(address, memAPI); } /// @@ -191,28 +191,26 @@ public LiveEntity GetLiveEntity(long address) /// private long FindAvailableEntitySlot(bool enemy = true) { - long start = enemy ? Game.EnemyEntitiesStart : Game.FriendlyEntitiesStart; + long address = enemy ? Game.EnemyEntitiesStart : Game.FriendlyEntitiesStart; int count = enemy ? Entities.EnemyEntitiesCount : Entities.FriendEntitiesCount; for (int i = 0; i < count; i++) { - LiveEntity entity = GetLiveEntity(start); + Entity entity = GetLiveEntity(address); bool reserved = false; for (int j = 0; j < Entities.ReservedSlots.Length; j++) { - if (start == Entities.ReservedSlots[j]) + if (address == Entities.ReservedSlots[j]) { reserved = true; } } - - - if (entity.HitboxWidth == 0 && entity.HitboxHeight == 0 && entity.Hp == 0 && entity.Damage == 0 && entity.AiId == 0 && !reserved) + if (entity.HitboxWidth == 0 && entity.HitboxHeight == 0 && entity.Hp == 0 && entity.Damage == 0 && entity.UpdateFunctionAddress == 0 && !reserved) { - return start; + return address; } - start += Entities.Offset; + address += Entities.Size; } return 0; @@ -224,7 +222,7 @@ public long SpawnEntity(Entity actor, bool enemy = true) if (slot > 0) { - memAPI.WriteByteRange(slot, actor.Value); + memAPI.WriteByteRange(slot, actor.Data); } return slot; diff --git a/SotnApi/SotnApi/GameApi.cs b/SotnApi/SotnApi/GameApi.cs index dc75fab..c525dbb 100644 --- a/SotnApi/SotnApi/GameApi.cs +++ b/SotnApi/SotnApi/GameApi.cs @@ -20,136 +20,136 @@ public GameApi(IMemoryApi? memAPI) if (memAPI == null) { throw new ArgumentNullException(nameof(memAPI)); } this.memAPI = memAPI; - ZoneTransitions = new Dictionary>() + ZoneTransitions = new Dictionary>() { { - Zone.CastleEntrance, - new Dictionary() + Stage.CastleEntrance, + new Dictionary() { - { Zone.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Zone.MarbleGallery], memAPI) }, - { Zone.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Zone.UndergroundCaverns], memAPI) }, - { Zone.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Zone.AlchemyLaboratory], memAPI) }, - { Zone.Warp, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Zone.Warp], memAPI) }, + { Stage.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Stage.MarbleGallery], memAPI) }, + { Stage.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Stage.UndergroundCaverns], memAPI) }, + { Stage.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Stage.AlchemyLaboratory], memAPI) }, + { Stage.Warp, new ZoneTransition(ZoneTransitionAddresses.CastleEntrance[Stage.Warp], memAPI) }, } }, { - Zone.AlchemyLaboratory, - new Dictionary() + Stage.AlchemyLaboratory, + new Dictionary() { - { Zone.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Zone.CastleEntrance], memAPI) }, - { Zone.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Zone.MarbleGallery], memAPI) }, - { Zone.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Zone.RoyalChapel], memAPI) }, + { Stage.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Stage.CastleEntrance], memAPI) }, + { Stage.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Stage.MarbleGallery], memAPI) }, + { Stage.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.AlchemyLaboratory[Stage.RoyalChapel], memAPI) }, } }, { - Zone.Colosseum, - new Dictionary() + Stage.Colosseum, + new Dictionary() { - {Zone.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.Colosseum[Zone.RoyalChapel], memAPI)}, - {Zone.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.Colosseum[Zone.OlroxsQuarters], memAPI)}, + {Stage.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.Colosseum[Stage.RoyalChapel], memAPI)}, + {Stage.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.Colosseum[Stage.OlroxsQuarters], memAPI)}, } }, { - Zone.OlroxsQuarters, - new Dictionary() + Stage.OlroxsQuarters, + new Dictionary() { - { Zone.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Zone.MarbleGallery], memAPI) }, - { Zone.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Zone.RoyalChapel], memAPI) }, - { Zone.Colosseum, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Zone.Colosseum], memAPI) }, - { Zone.Warp, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Zone.Warp], memAPI) }, + { Stage.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Stage.MarbleGallery], memAPI) }, + { Stage.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Stage.RoyalChapel], memAPI) }, + { Stage.Colosseum, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Stage.Colosseum], memAPI) }, + { Stage.Warp, new ZoneTransition(ZoneTransitionAddresses.OlroxsQuarters[Stage.Warp], memAPI) }, } }, { - Zone.RoyalChapel, - new Dictionary() + Stage.RoyalChapel, + new Dictionary() { - { Zone.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Zone.OlroxsQuarters], memAPI) }, - { Zone.Colosseum, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Zone.Colosseum], memAPI) }, - { Zone.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Zone.AlchemyLaboratory], memAPI) }, - { Zone.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Zone.CastleKeep], memAPI) }, + { Stage.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Stage.OlroxsQuarters], memAPI) }, + { Stage.Colosseum, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Stage.Colosseum], memAPI) }, + { Stage.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Stage.AlchemyLaboratory], memAPI) }, + { Stage.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.RoyalChapel[Stage.CastleKeep], memAPI) }, } }, { - Zone.Warp, - new Dictionary() + Stage.Warp, + new Dictionary() { - { Zone.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.Warp[Zone.CastleKeep], memAPI) }, - { Zone.OuterWall, new ZoneTransition(ZoneTransitionAddresses.Warp[Zone.OuterWall], memAPI) }, - { Zone.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.Warp[Zone.OlroxsQuarters], memAPI) }, - { Zone.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.Warp[Zone.CastleEntrance], memAPI) }, - { Zone.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.Warp[Zone.AbandonedMine], memAPI) }, + { Stage.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.Warp[Stage.CastleKeep], memAPI) }, + { Stage.OuterWall, new ZoneTransition(ZoneTransitionAddresses.Warp[Stage.OuterWall], memAPI) }, + { Stage.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.Warp[Stage.OlroxsQuarters], memAPI) }, + { Stage.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.Warp[Stage.CastleEntrance], memAPI) }, + { Stage.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.Warp[Stage.AbandonedMine], memAPI) }, } }, { - Zone.MarbleGallery, - new Dictionary() + Stage.MarbleGallery, + new Dictionary() { - { Zone.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.AlchemyLaboratory], memAPI) }, - { Zone.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.OlroxsQuarters], memAPI) }, - { Zone.OuterWall, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.OuterWall], memAPI) }, - { Zone.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.CastleEntrance], memAPI) }, - { Zone.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.UndergroundCaverns], memAPI) }, - { Zone.CenterCube, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Zone.CenterCube], memAPI) }, + { Stage.AlchemyLaboratory, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.AlchemyLaboratory], memAPI) }, + { Stage.OlroxsQuarters, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.OlroxsQuarters], memAPI) }, + { Stage.OuterWall, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.OuterWall], memAPI) }, + { Stage.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.CastleEntrance], memAPI) }, + { Stage.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.UndergroundCaverns], memAPI) }, + { Stage.CenterCube, new ZoneTransition(ZoneTransitionAddresses.MarbleGallery[Stage.CenterCube], memAPI) }, } }, { - Zone.LongLibrary, - new Dictionary() + Stage.LongLibrary, + new Dictionary() { - { Zone.OuterWall, new ZoneTransition(ZoneTransitionAddresses.LongLibrary[Zone.OuterWall], memAPI) }, + { Stage.OuterWall, new ZoneTransition(ZoneTransitionAddresses.LongLibrary[Stage.OuterWall], memAPI) }, } }, { - Zone.ClockTower, - new Dictionary() + Stage.ClockTower, + new Dictionary() { - { Zone.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.ClockTower[Zone.CastleKeep], memAPI) }, - { Zone.OuterWall, new ZoneTransition(ZoneTransitionAddresses.ClockTower[Zone.OuterWall], memAPI) }, + { Stage.CastleKeep, new ZoneTransition(ZoneTransitionAddresses.ClockTower[Stage.CastleKeep], memAPI) }, + { Stage.OuterWall, new ZoneTransition(ZoneTransitionAddresses.ClockTower[Stage.OuterWall], memAPI) }, } }, { - Zone.CastleKeep, - new Dictionary() + Stage.CastleKeep, + new Dictionary() { - { Zone.ClockTower, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Zone.ClockTower], memAPI) }, - { Zone.Warp, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Zone.Warp], memAPI) }, - { Zone.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Zone.RoyalChapel], memAPI) }, + { Stage.ClockTower, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Stage.ClockTower], memAPI) }, + { Stage.Warp, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Stage.Warp], memAPI) }, + { Stage.RoyalChapel, new ZoneTransition(ZoneTransitionAddresses.CastleKeep[Stage.RoyalChapel], memAPI) }, } }, { - Zone.UndergroundCaverns, - new Dictionary() + Stage.UndergroundCaverns, + new Dictionary() { - { Zone.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Zone.AbandonedMine], memAPI) }, - { Zone.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Zone.MarbleGallery], memAPI) }, - { Zone.Nightmare, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Zone.Nightmare], memAPI) }, - { Zone.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Zone.CastleEntrance], memAPI) }, + { Stage.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Stage.AbandonedMine], memAPI) }, + { Stage.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Stage.MarbleGallery], memAPI) }, + { Stage.Nightmare, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Stage.Nightmare], memAPI) }, + { Stage.CastleEntrance, new ZoneTransition(ZoneTransitionAddresses.UndergroundCaverns[Stage.CastleEntrance], memAPI) }, } }, { - Zone.AbandonedMine, - new Dictionary() + Stage.AbandonedMine, + new Dictionary() { - { Zone.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Zone.UndergroundCaverns], memAPI) }, - { Zone.Warp, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Zone.Warp], memAPI) }, - { Zone.Catacombs, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Zone.Catacombs], memAPI) }, + { Stage.UndergroundCaverns, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Stage.UndergroundCaverns], memAPI) }, + { Stage.Warp, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Stage.Warp], memAPI) }, + { Stage.Catacombs, new ZoneTransition(ZoneTransitionAddresses.AbandonedMine[Stage.Catacombs], memAPI) }, } }, { - Zone.Catacombs, - new Dictionary() + Stage.Catacombs, + new Dictionary() { - { Zone.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.Catacombs[Zone.AbandonedMine], memAPI) }, + { Stage.AbandonedMine, new ZoneTransition(ZoneTransitionAddresses.Catacombs[Stage.AbandonedMine], memAPI) }, } }, { - Zone.OuterWall, - new Dictionary() + Stage.OuterWall, + new Dictionary() { - { Zone.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Zone.MarbleGallery], memAPI) }, - { Zone.LongLibrary, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Zone.LongLibrary], memAPI) }, - { Zone.Warp, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Zone.Warp], memAPI) }, - { Zone.ClockTower, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Zone.ClockTower], memAPI) }, + { Stage.MarbleGallery, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Stage.MarbleGallery], memAPI) }, + { Stage.LongLibrary, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Stage.LongLibrary], memAPI) }, + { Stage.Warp, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Stage.Warp], memAPI) }, + { Stage.ClockTower, new ZoneTransition(ZoneTransitionAddresses.OuterWall[Stage.ClockTower], memAPI) }, } }, }; @@ -187,7 +187,7 @@ public Character CurrentCharacter } } - public Dictionary> ZoneTransitions { get; } + public Dictionary> ZoneTransitions { get; } public bool SecondCastle { @@ -213,11 +213,11 @@ public uint RoomY } } - public uint Zone2 + public uint StageId { get { - return memAPI.ReadByte(Game.Zone2); + return memAPI.ReadByte(Game.StageId); } } diff --git a/SotnApi/SotnApi/Interfaces/IAlucardApi.cs b/SotnApi/SotnApi/Interfaces/IAlucardApi.cs index 2b1ac4d..400570f 100644 --- a/SotnApi/SotnApi/Interfaces/IAlucardApi.cs +++ b/SotnApi/SotnApi/Interfaces/IAlucardApi.cs @@ -168,6 +168,7 @@ public interface IAlucardApi bool HasControl(); bool HasHitbox(); void Heal(uint ammount); + void InstantDeath(); bool EffectActive(); void ForceLibraryCard(); void ActivateStopwatch(); diff --git a/SotnApi/SotnApi/Interfaces/IEntityApi.cs b/SotnApi/SotnApi/Interfaces/IEntityApi.cs index 048f088..1e598f9 100644 --- a/SotnApi/SotnApi/Interfaces/IEntityApi.cs +++ b/SotnApi/SotnApi/Interfaces/IEntityApi.cs @@ -36,19 +36,19 @@ public interface IEntityApi /// long FindEntityFrom(List actors, bool enemy = true); /// - /// Scans memory for all active actors. + /// Scans memory for all active entities. /// /// /// A list of addresses where the enemy entities start. /// - List GetAllActors(); + List GetAllEntities(); /// - /// Scans memory for all active actors, that match the example actors. + /// Scans memory for all active entities, that match . /// /// /// A list of addresses where the enemy entities start. /// - public List GetAllActors(List actors); + public List GetAllEntities(List actors); /// /// Byte range of the enemy entity. /// @@ -56,7 +56,7 @@ public interface IEntityApi /// /// A LiveActor instance that can be used to edit the in-game entity. /// - LiveEntity GetLiveEntity(long address); + Entity GetLiveEntity(long address); /// /// Copies the actor data to memory. Spawning it in-game. /// diff --git a/SotnApi/SotnApi/Interfaces/IGameApi.cs b/SotnApi/SotnApi/Interfaces/IGameApi.cs index ac20c09..44fc760 100644 --- a/SotnApi/SotnApi/Interfaces/IGameApi.cs +++ b/SotnApi/SotnApi/Interfaces/IGameApi.cs @@ -30,7 +30,7 @@ public interface IGameApi /// /// Zone transitions. Doesn't include cutscene and boss transitions. /// - public Dictionary> ZoneTransitions { get; } + public Dictionary> ZoneTransitions { get; } /// /// Index for the current area or subarea. /// @@ -72,9 +72,9 @@ public interface IGameApi /// uint RoomY { get; } /// - /// Zone Byte2. + /// Current StageId. /// - uint Zone2 { get; } + uint StageId { get; } /// /// True of the map is open. /// diff --git a/SotnApi/SotnApi/Models/Entity.cs b/SotnApi/SotnApi/Models/Entity.cs index 717d197..7cafd82 100644 --- a/SotnApi/SotnApi/Models/Entity.cs +++ b/SotnApi/SotnApi/Models/Entity.cs @@ -1,265 +1,483 @@ -using SotnApi.Constants.Values.Game; +using BizHawk.Client.Common; +using SotnApi.Constants.Values.Game; using System; using System.Collections.Generic; namespace SotnApi.Models { /// - /// An entity object that can be rendered in-game. Enemies, projectiles, items, hitboxes, interactable objects. + /// An object that can be rendered in-game or a live in-memory instance. Enemies, projectiles, items, hitboxes, interactable objects. /// public sealed class Entity { + private readonly IMemoryApi memAPI; + private bool isLive = false; + + public Entity(long address, IMemoryApi? memAPI) + { + Address = address; + if (memAPI == null) { throw new ArgumentNullException("Memory API is null"); } + this.memAPI = memAPI; + this.isLive = true; + } public Entity(List? entity = null) { if (entity != null) { - this.Value = entity; + this.Data = entity; } else { - Value = new List(); - for (int i = 0; i <= Entities.Size; i++) - { - Value.Add(0); - } + Data = new List(new byte[Entities.Size]); } } - public List Value { get; set; } - public ushort Xpos + public long Address { get; set; } + public List Data { get; set; } + + public double Xpos { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.XposOffset); + return ReadFixedPoint1616(Entities.Xpos); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.XposOffset + i] = valueBytes[i]; - } + WriteU32(Entities.Xpos, (uint)value); } } - - public ushort Ypos + public double Ypos { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.YposOffset); + return ReadFixedPoint1616(Entities.Ypos); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.YposOffset + i] = valueBytes[i]; - } + WriteU32(Entities.Ypos, (uint)value); } } - - public ushort SpeedHorizontalFract + public double AccelerationX { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.SpeedFractOffset); + return ReadFixedPoint1616(Entities.AccelerationX); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.SpeedFractOffset + i] = valueBytes[i]; - } + WriteU32(Entities.AccelerationX, (uint)value); } } - - public ushort SpeedHorizontal + public double AccelerationY { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.SpeedWholeOffset); + return ReadFixedPoint1616(Entities.AccelerationY); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.SpeedWholeOffset + i] = valueBytes[i]; - } + WriteU32(Entities.AccelerationY, (uint)value); } } - - public ushort SpeedVerticalFract + public ushort Facing { get { - return Value[Entities.SpeedVerticalFractOffset]; + return ReadU16(Entities.Facing); } set { - Value[Entities.SpeedVerticalFractOffset] = (byte)value; + WriteU16(Entities.Facing, value); } } - - public ushort SpeedVertical + public byte Palette { get { - return Value[Entities.SpeedVerticalWholeOffset]; + return ReadByte(Entities.Palette); } set { - Value[Entities.SpeedVerticalWholeOffset] = (byte)value; + WriteByte(Entities.Palette, value); } } - - public ushort AutoToggle + public byte BlendMode { get { - return Value[Entities.HitboxAutoToggleOffset]; + return ReadByte(Entities.BlendMode); } set { - Value[Entities.HitboxAutoToggleOffset] = (byte)value; + WriteByte(Entities.BlendMode, value); } } - - public ushort Hp + public ushort ZPriority { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.HpOffset); + return ReadU16(Entities.ZPriority); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.HpOffset + i] = valueBytes[i]; - } + WriteU16(Entities.ZPriority, value); } } - - public ushort Def + public ushort ObjectId { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.DefOffset); + return ReadU16(Entities.ObjectId); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.DefOffset + i] = valueBytes[i]; - } + WriteU16(Entities.ObjectId, value); } } - - public ushort Damage + public uint UpdateFunctionAddress { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.DamageOffset); + return ReadU32(Entities.UpdateFunctionAddress); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.DamageOffset + i] = valueBytes[i]; - } + WriteU32(Entities.UpdateFunctionAddress, value); } } - - public uint DamageTypeA + public ushort Step { get { - return Value[Entities.DamageTypeAOffset]; + return ReadU16(Entities.Step); } set { - Value[Entities.DamageTypeAOffset] = (byte)value; + WriteU16(Entities.Step, value); } } - - public uint DamageTypeB + public ushort Step2 { get { - return Value[Entities.DamageTypeBOffset]; + return ReadU16(Entities.Step2); } set { - Value[Entities.DamageTypeBOffset] = (byte)value; + WriteU16(Entities.Step2, value); } } - - public ushort Palette + public ushort SubId { get { - return Value[Entities.PaletteOffset]; + return ReadU16(Entities.SubId); } set { - Value[Entities.PaletteOffset] = (byte)value; + WriteU16(Entities.SubId, value); } } - - public ushort ColorMode + public ushort ObjectRoomIndex { get { - return Value[Entities.ColorModeOffset]; + return ReadU16(Entities.ObjectRoomIndex); } set { - Value[Entities.ColorModeOffset] = (byte)value; + WriteU16(Entities.ObjectRoomIndex, value); } } - //AI id - public ushort AiId + public uint Flags { get { - return BitConverter.ToUInt16(Value.ToArray(), Entities.AiIdOffset); + return ReadU32(Entities.Flags); } set { - byte[] valueBytes = BitConverter.GetBytes(value); - for (int i = 0; i < 2; i++) - { - Value[Entities.AiIdOffset + i] = valueBytes[i]; - } + WriteU32(Entities.Flags, value); + } + } + public ushort EnemyIndex + { + get + { + return ReadU16(Entities.EnemyIndex); + } + set + { + WriteU16(Entities.EnemyIndex, value); + } + } + public byte AutoToggle + { + get + { + return ReadByte(Entities.HitboxAutoToggle); + } + set + { + WriteByte(Entities.HitboxAutoToggle, value); + } + } + public short Hp + { + get + { + return ReadS16(Entities.Hp); + } + set + { + WriteS16(Entities.Hp, value); + } + } + public ushort Damage + { + get + { + return ReadU16(Entities.Damage); + } + set + { + WriteU16(Entities.Damage, value); + } + } + public byte DamageTypeA + { + get + { + return ReadByte(Entities.DamageTypeA); + } + set + { + WriteByte(Entities.DamageTypeA, value); + } + } + public byte DamageTypeB + { + get + { + return ReadByte(Entities.DamageTypeB); + } + set + { + WriteByte(Entities.DamageTypeB, value); } } - public ushort HitboxWidth { get { - return Value[Entities.HitboxWidthOffset]; + return Data[Entities.HitboxWidth]; } set { - Value[Entities.HitboxWidthOffset] = (byte)value; + Data[Entities.HitboxWidth] = (byte)value; } } - public ushort HitboxHeight { get { - return Value[Entities.HitboxHeightOffset]; + return Data[Entities.HitboxHeight]; + } + set + { + Data[Entities.HitboxHeight] = (byte)value; + } + } + public byte InvincibilityFrames + { + get + { + return ReadByte(Entities.InvincibilityFrames); + } + set + { + WriteByte(Entities.InvincibilityFrames, value); + } + } + public ushort AnimationFrameIndex + { + get + { + return Data[Entities.AnimationFrameIndex]; } set { - Value[Entities.HitboxHeightOffset] = (byte)value; + Data[Entities.AnimationFrameIndex] = (byte)value; } } + public ushort AnimationFrameDuration + { + get + { + return Data[Entities.AnimationFrameDuration]; + } + set + { + Data[Entities.AnimationFrameDuration] = (byte)value; + } + } + public ushort AnimationSet + { + get + { + return Data[Entities.AnimationSet]; + } + set + { + Data[Entities.AnimationSet] = (byte)value; + } + } + public ushort AnimationCurrentFrame + { + get + { + return Data[Entities.AnimationCurrentFrame]; + } + set + { + Data[Entities.AnimationCurrentFrame] = (byte)value; + } + } + + private void WriteByte(int offset, byte value) + { + if (isLive) + { + memAPI.WriteByte(Address + offset, value); + } + else + { + Data[Entities.Palette] = value; + } + } + private void WriteU16(int offset, ushort value) + { + if (isLive) + { + memAPI.WriteU16(Address + offset, value); + } + else + { + for (int i = 0; i < 2; i++) + { + byte[] valueBytes = BitConverter.GetBytes(value); + Data[offset + i] = valueBytes[i]; + + } + } + } + private void WriteU32(int offset, uint value) + { + if (isLive) + { + memAPI.WriteU32(Address + offset, value); + } + else + { + for (int i = 0; i < 4; i++) + { + byte[] valueBytes = BitConverter.GetBytes(value); + Data[offset + i] = valueBytes[i]; + + } + } + } + private void WriteS16(int offset, short value) + { + if (isLive) + { + memAPI.WriteS16(Address + offset, value); + } + else + { + for (int i = 0; i < 2; i++) + { + byte[] valueBytes = BitConverter.GetBytes(value); + Data[offset + i] = valueBytes[i]; + + } + } + } + private void WriteS32(int offset, int value) + { + if (isLive) + { + memAPI.WriteS32(Address + offset, value); + } + else + { + for (int i = 0; i < 4; i++) + { + byte[] valueBytes = BitConverter.GetBytes(value); + Data[offset + i] = valueBytes[i]; + + } + } + } + private byte ReadByte(int offset) + { + if (isLive) + { + return (byte)memAPI.ReadByte(Address + offset); + } + else + { + return Data[offset]; + } + } + private ushort ReadU16(int offset) + { + if (isLive) + { + return (ushort)memAPI.ReadU16(Address + offset); + } + else + { + return BitConverter.ToUInt16(Data.ToArray(), offset); + } + } + private uint ReadU32(int offset) + { + if (isLive) + { + return memAPI.ReadU32(Address + offset); + } + else + { + return BitConverter.ToUInt32(Data.ToArray(), offset); + } + } + private short ReadS16(int offset) + { + if (isLive) + { + return (short)memAPI.ReadS16(Address + offset); + } + else + { + return BitConverter.ToInt16(Data.ToArray(), offset); + } + } + private int ReadS32(int offset) + { + if (isLive) + { + return memAPI.ReadS32(Address + offset); + } + else + { + return BitConverter.ToInt32(Data.ToArray(), offset); + } + } + private double ReadFixedPoint1616(int offset) + { + uint rawValue = ReadU32(offset); + return ((int)rawValue / 65536.0); + } } } diff --git a/SotnApi/SotnApi/Models/LiveEntity.cs b/SotnApi/SotnApi/Models/LiveEntity.cs deleted file mode 100644 index 04b3256..0000000 --- a/SotnApi/SotnApi/Models/LiveEntity.cs +++ /dev/null @@ -1,255 +0,0 @@ -using BizHawk.Client.Common; -using SotnApi.Constants.Values.Game; -using System; - -namespace SotnApi.Models -{ - /// - /// A live entity object rendered in-game. Enemies, projectiles, items, hitboxes, interactable objects. - /// - public sealed class LiveEntity - { - private readonly IMemoryApi memAPI; - - public LiveEntity(long address, IMemoryApi? memAPI) - { - Address = address; - if (memAPI == null) { throw new ArgumentNullException("Memory API is null"); } - this.memAPI = memAPI; - } - - public long Address { get; set; } - - public uint Xpos - { - get - { - return memAPI.ReadU16(Address + Entities.XposOffset); - } - set - { - memAPI.WriteU16(Address + Entities.XposOffset, value); - } - } - - public uint Ypos - { - get - { - return memAPI.ReadU16(Address + Entities.YposOffset); - } - set - { - memAPI.WriteU16(Address + Entities.YposOffset, value); - } - } - - public uint SpeedHorizontalFract - { - get - { - return memAPI.ReadU16(Address + Entities.SpeedFractOffset); - } - set - { - memAPI.WriteU16(Address + Entities.SpeedFractOffset, value); - } - } - - public int SpeedHorizontal - { - get - { - return (int)memAPI.ReadS16(Address + Entities.SpeedWholeOffset); - } - set - { - memAPI.WriteS16(Address + Entities.SpeedWholeOffset, value); - } - } - - public uint SpeedVerticalFract - { - get - { - return memAPI.ReadByte(Address + Entities.SpeedVerticalFractOffset); - } - set - { - memAPI.WriteByte(Address + Entities.SpeedVerticalFractOffset, value); - } - } - - public int SpeedVertical - { - get - { - return (int)(sbyte)memAPI.ReadByte(Address + Entities.SpeedVerticalWholeOffset); - } - set - { - if (value < 0) - { - memAPI.WriteByte(Address + Entities.SpeedVerticalNegativeOffset, 0xFF); - } - memAPI.WriteByte(Address + Entities.SpeedVerticalWholeOffset, (byte)(sbyte)value); - } - } - - public uint AutoToggle - { - get - { - return memAPI.ReadByte(Address + Entities.HitboxAutoToggleOffset); - } - set - { - memAPI.WriteByte(Address + Entities.HitboxAutoToggleOffset, value); - } - } - - public int Hp - { - get - { - return memAPI.ReadS16(Address + Entities.HpOffset); - } - set - { - memAPI.WriteS16(Address + Entities.HpOffset, value); - } - } - - public uint Def - { - get - { - return memAPI.ReadByte(Address + Entities.DefOffset); - } - set - { - memAPI.WriteByte(Address + Entities.DefOffset, value); - } - } - - public uint Damage - { - get - { - return memAPI.ReadU16(Address + Entities.DamageOffset); - } - set - { - memAPI.WriteU16(Address + Entities.DamageOffset, value); - } - } - - public uint DamageTypeA - { - get - { - return memAPI.ReadByte(Address + Entities.DamageTypeAOffset); - } - set - { - memAPI.WriteByte(Address + Entities.DamageTypeAOffset, value); - } - } - - public uint DamageTypeB - { - get - { - return memAPI.ReadByte(Address + Entities.DamageTypeBOffset); - } - set - { - memAPI.WriteByte(Address + Entities.DamageTypeBOffset, value); - } - } - - public uint Palette - { - get - { - return memAPI.ReadByte(Address + Entities.PaletteOffset); - } - set - { - memAPI.WriteByte(Address + Entities.PaletteOffset, value); - } - } - - public uint ColorMode - { - get - { - return memAPI.ReadByte(Address + Entities.ColorModeOffset); - } - set - { - memAPI.WriteByte(Address + Entities.ColorModeOffset, value); - } - } - - public uint AiId - { - get - { - return memAPI.ReadU16(Address + Entities.AiIdOffset); - } - set - { - memAPI.WriteU16(Address + Entities.AiIdOffset, value); - } - } - - public uint LockOn - { - get - { - return memAPI.ReadU16(Address + Entities.LockOnOffset); - } - set - { - memAPI.WriteU16(Address + Entities.LockOnOffset, value); - } - } - - public uint HitboxWidth - { - get - { - return memAPI.ReadByte(Address + Entities.HitboxWidthOffset); - } - set - { - memAPI.WriteByte(Address + Entities.HitboxWidthOffset, value); - } - } - - public uint HitboxHeight - { - get - { - return memAPI.ReadByte(Address + Entities.HitboxHeightOffset); - } - set - { - memAPI.WriteByte(Address + Entities.HitboxHeightOffset, value); - } - } - - public uint InvincibilityFrames - { - get - { - return memAPI.ReadByte(Address + Entities.InvincibilityFramesOffset); - } - set - { - memAPI.WriteByte(Address + Entities.InvincibilityFramesOffset, value); - } - } - } -} diff --git a/SotnApi/SotnApi/Models/SearchableActor.cs b/SotnApi/SotnApi/Models/SearchableActor.cs index c21bd46..2e87262 100644 --- a/SotnApi/SotnApi/Models/SearchableActor.cs +++ b/SotnApi/SotnApi/Models/SearchableActor.cs @@ -6,7 +6,7 @@ public sealed class SearchableActor public int Xpos { get; set; } public int Ypos { get; set; } public int Hp { get; set; } - public int AiId { get; set; } + public int UpdateFunctionAddress { get; set; } public int Palette { get; set; } public int Damage { get; set; } public int HitboxWidth { get; set; } diff --git a/SotnApi/SotnApi/Models/TeleportZone.cs b/SotnApi/SotnApi/Models/TeleportZone.cs index 6a10900..794cf4c 100644 --- a/SotnApi/SotnApi/Models/TeleportZone.cs +++ b/SotnApi/SotnApi/Models/TeleportZone.cs @@ -1,6 +1,6 @@ namespace SotnApi.Models { - public sealed class TeleportZone + public sealed class TeleportDestination { public ushort Zone { get; set; } public int Xpos { get; set; } diff --git a/SotnApi/SotnApi/SotnApi.csproj b/SotnApi/SotnApi/SotnApi.csproj index 24e285e..246feb8 100644 --- a/SotnApi/SotnApi/SotnApi.csproj +++ b/SotnApi/SotnApi/SotnApi.csproj @@ -4,15 +4,16 @@ 9.0 enable net48 - 1.1.6 + 1.1.7 + True - D:\Programming\SotnApi\SotnApi\SotnApi\SotnApi.xml + $(OutputPath)./SotnApi.xml - D:\Programming\SotnApi\SotnApi\SotnApi\SotnApi.xml + $(OutputPath)./SotnApi.xml diff --git a/SotnApi/SotnApi/SotnApi.xml b/SotnApi/SotnApi/SotnApi.xml index a0feaca..3b7cf84 100644 --- a/SotnApi/SotnApi/SotnApi.xml +++ b/SotnApi/SotnApi/SotnApi.xml @@ -4,6 +4,11 @@ SotnApi + + + Spawning entities in these can cause relics not to spawn. + + Prologue Richter still counts as Alucard @@ -196,17 +201,17 @@ The address where the enemy entity starts. Or 0 if the enemy was not found. - + - Scans memory for all active actors. + Scans memory for all active entities. A list of addresses where the enemy entities start. - + - Scans memory for all active actors, that match the example actors. + Scans memory for all active entities, that match . A list of addresses where the enemy entities start. @@ -311,9 +316,9 @@ Y coordinate for the current highlighted room on the map. - + - Zone Byte2. + Current StageId. @@ -478,12 +483,7 @@ - An entity object that can be rendered in-game. Enemies, projectiles, items, hitboxes, interactable objects. - - - - - A live entity object rendered in-game. Enemies, projectiles, items, hitboxes, interactable objects. + An object that can be rendered in-game or a live in-memory instance. Enemies, projectiles, items, hitboxes, interactable objects.