Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
TalicZealot committed Mar 14, 2021
1 parent e35ba58 commit aaa572b
Show file tree
Hide file tree
Showing 47 changed files with 2,721 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
# Mono auto generated files
mono_crash.*

#BizHawk folder
BizHawk/

# Build results
[Dd]ebug/
[Dd]ebugPublic/
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
# SotnApi
# SotN Api

An api for reading and manipulating game entities in ram through the BizHawk API.

## Usage
Download the dll from the release and reference in your project.
Create an instance of the api you want to use by passing the IMemoryApi provided by BizHawk to an ExternalTool at runtime.

## Building
Put a BizHawk folder in the folder that contains SotnApi.sln to reference the proper dll files.

## Contributions
I will continue to add functionality, but feel free to do so and make a pull request.
Please write unit tests and document your interfaces when appropriate.

## APIs
* ActorApi (Spawns, finds and edits actor entities in memory.)
* AlucardApi (Returns or sets Alucard's stats, equipment, statuses, timers)
* GameApi (Sets or returns different game-specific values such as the current area, zone, room. Can override string values. Can Respawn Bosses etc.)
* RenderingApi (Checks or draws on the minimap in VRAM)
33 changes: 33 additions & 0 deletions SotnApi/ActorApiTests/ActorApiTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SotnApi\SotnApi.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="BizHawk.Client.Common">
<HintPath>..\BizHawk\dll\BizHawk.Client.Common.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions SotnApi/ActorApiTests/ConstructorShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using SotnApi;
using System;
using Xunit;

namespace ActorApiTests
{
public class ConstructorShould
{
[Fact]
public void ThrowArgumentNullException_WhenMemApiIsNull()
{
Assert.Throws<ArgumentNullException>(() => new ActorApi(null));
}
}
}
78 changes: 78 additions & 0 deletions SotnApi/ActorApiTests/FindEnemyShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using BizHawk.Client.Common;
using SotnApi.Constants.Values.Game;
using SotnApi.Constants.Addresses;
using NSubstitute;
using SotnApi;
using SotnApi.Interfaces;
using System;
using Xunit;

namespace ActorApiTests
{
public class FindEnemyShould
{
[Fact]
public void ThrowArgumentOutOfRangeException_WhenMinHpIsNegative()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
string message = "minHp can't be negative\r\nParameter name: minHp";
//Act
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => classUnderTest.FindEnemy(-1, 1));
//Assert
Assert.Equal(message, exception.Message);
}

[Fact]
public void ThrowArgumentOutOfRangeException_WhenMaxHpIsLowerThanOne()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
string message = "maxHp must be greater than 0\r\nParameter name: maxHp";
//Act
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => classUnderTest.FindEnemy(1, 0));
//Assert
Assert.Equal(message, exception.Message);
}

[Fact]
public void CallReadByteMethodOfMemAPI_ExactlyMaximumTimes()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
//Act
classUnderTest.FindEnemy(1, 1);
//Assert
mockedMemAPI.Received(3 * Actors.Count).ReadByte(Arg.Any<long>());
}

[Fact]
public void ReturnZeroWhenEnemyWasNotFound()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
//Act
long enemy = classUnderTest.FindEnemy(1, 10);
//Assert
Assert.Equal(0, enemy);
}
[Fact]
public void ReturnCorrectAddressWhenEnemyWasFound()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
mockedMemAPI
.ReadByte(Arg.Any<long>())
.ReturnsForAnyArgs<uint>(3);
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
//Act
long enemy = classUnderTest.FindEnemy(1, 1000);
//Assert
Assert.Equal(Game.ActorsStart, enemy);
}
}
}
28 changes: 28 additions & 0 deletions SotnApi/ActorApiTests/SpawnActorShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using BizHawk.Client.Common;
using SotnApi.Constants.Values.Game;
using SotnApi.Constants.Addresses;
using NSubstitute;
using SotnApi;
using SotnApi.Interfaces;
using System;
using Xunit;
using SotnApi.Models;

namespace ActorApiTests
{
public class SpawnActorShould
{
[Fact]
public void CallsWriteByteRangeMethodOfMemAPI_ExactlyOnce()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IActorApi classUnderTest = new ActorApi(mockedMemAPI);
Actor blankActor = new Actor();
//Act
classUnderTest.SpawnActor(blankActor);
//Assert
mockedMemAPI.Received(1).WriteByteRange(Arg.Any<long>(), blankActor.Value);
}
}
}
33 changes: 33 additions & 0 deletions SotnApi/AlucardApiTests/AlucardApiTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SotnApi\SotnApi.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="BizHawk.Client.Common">
<HintPath>..\..\SotnRandoTools\BizHawk\dll\BizHawk.Client.Common.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions SotnApi/AlucardApiTests/ConstructorShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using SotnApi;
using System;
using Xunit;

namespace AlucardApiTests
{
public class ConstructorShould
{
[Fact]
public void ThrowArgumentNullException_WhenMemApiIsNull()
{
Assert.Throws<ArgumentNullException>(() => new AlucardApi(null));
}
}
}
65 changes: 65 additions & 0 deletions SotnApi/AlucardApiTests/GrantItemByNameShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using BizHawk.Client.Common;
using NSubstitute;
using SotnApi;
using SotnApi.Interfaces;
using System;
using Xunit;

namespace AlucardApiTests
{
public class GrantItemByNameShould
{
[Fact]
public void ThrowArgumentNullException_WhenNameIsNull()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act&Assert
Assert.Throws<ArgumentNullException>(() => classUnderTest.GrantItemByName(null));
}

[Fact]
public void ThrowArgumentExceptionWithCorrectMessage_WhenNameIsInvalid()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
string message = "Invalid item name!";
//Act
var exception = Assert.Throws<ArgumentException>(() => classUnderTest.GrantItemByName("invalid item"));
//Act&Assert
Assert.Equal(message, exception.Message);
}

[Fact]
public void CallReadByteMethodOfMemAPI_ExactlyOneTime()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
mockedMemAPI
.ReadByte(Arg.Any<long>())
.ReturnsForAnyArgs<uint>(1);
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act
classUnderTest.GrantItemByName("empty hand");
//Assert
mockedMemAPI.Received(1).ReadByte(Arg.Any<long>());
}

[Fact]
public void CallWriteByteMethodOfMemAPI_ExactlyOneTime()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
mockedMemAPI
.ReadByte(Arg.Any<long>())
.ReturnsForAnyArgs<uint>(0);
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act
classUnderTest.GrantItemByName("empty hand");
//Assert
mockedMemAPI.Received(1).WriteByte(Arg.Any<long>(), 1);
}
}
}
77 changes: 77 additions & 0 deletions SotnApi/AlucardApiTests/HasItemInInventoryShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using BizHawk.Client.Common;
using NSubstitute;
using SotnApi;
using SotnApi.Interfaces;
using System;
using Xunit;

namespace AlucardApiTests
{
public class HasItemInInventoryShould
{
[Fact]
public void ThrowArgumentNullException_WhenNameIsNull()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act&Assert
Assert.Throws<ArgumentNullException>(() => classUnderTest.HasItemInInventory(null));
}

[Fact]
public void ThrowArgumentExceptionWithCorrectMessage_WhenNameIsInvalid()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
string message = "Invalid item name!";
//Act
var exception = Assert.Throws<ArgumentException>(() => classUnderTest.HasItemInInventory("invalid item"));
//Act&Assert
Assert.Equal(message, exception.Message);
}

[Fact]
public void CallReadByteMethodOfMemAPI_ExactlyOneTime()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act
classUnderTest.HasItemInInventory("empty hand");
//Assert
mockedMemAPI.Received(1).ReadByte(Arg.Any<long>());
}

[Fact]
public void ReturnFalseWhenItemCountIsZero()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
mockedMemAPI
.ReadByte(Arg.Any<long>())
.ReturnsForAnyArgs<uint>(0);
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act
bool result = classUnderTest.HasItemInInventory("empty hand");
//Assert
Assert.False(result);
}

[Fact]
public void ReturnTrueWhenItemCountIsMoreThanZero()
{
//Arrange
var mockedMemAPI = Substitute.For<IMemoryApi>();
mockedMemAPI
.ReadByte(Arg.Any<long>())
.ReturnsForAnyArgs<uint>(1);
IAlucardApi classUnderTest = new AlucardApi(mockedMemAPI);
//Act
bool result = classUnderTest.HasItemInInventory("empty hand");
//Assert
Assert.True(result);
}
}
}
Loading

0 comments on commit aaa572b

Please sign in to comment.