Emik.Manual 1.0.0

dotnet add package Emik.Manual --version 1.0.0
                    
NuGet\Install-Package Emik.Manual -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Emik.Manual" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Emik.Manual" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Emik.Manual" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Emik.Manual --version 1.0.0
                    
#r "nuget: Emik.Manual, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Emik.Manual&version=1.0.0
                    
Install Emik.Manual as a Cake Addin
#tool nuget:?package=Emik.Manual&version=1.0.0
                    
Install Emik.Manual as a Cake Tool

Icon

Emik.Manual

NuGet package License

.NET 9.0+ library to programmatically create a manual .apworld for use in Archipelago.

This project has a dependency to Emik.Morsels, if you are building this project, refer to its README first.



Examples

This implementation is for the video game Space Invaders: Infinity Gene. To run this yourself, it requires Emik.Morsels, OR just this file if you're simply running it in a REPL. For additional examples, take a look at Emik.Manual.Examples, each project ranging in complexity.

using Emik.Manual;
using Emik.Manual.Domains;

static IAsyncEnumerable<SplitMemory<char, char, MatchOne>> Read([Match("^[^<>:\"/\\\\|?*]+$")] string path)
{
    var found = Path.Join(Environment.CurrentDirectory)
       .FindPathToNull(Path.GetDirectoryName)
       .Select(x => Path.Join(x, path))
       .FirstOrDefault(File.Exists);

    return File.ReadLinesAsync(found ?? throw new FileNotFoundException(null, path)).Select(x => x.SplitOn(','));
}

World world = new();

ImmutableArray<(string Name, int Count)> arms =
    [("RAPID SHOT", 1), ("SEARCH LASER", 1), ("WAVE", 1), ("LOCK-ON", 4), ("GRAVITY", 2), ("ROUND", 1), ("CLASSIC", 4)];

var armCategory = world.Category("ARMS");
var starterArmCategory = world.Category("STARTER ARMS", true);

foreach (var (name, count) in arms)
    world.Item(
        $"PROGRESSIVE {name} ({count})",
        count is 1 ? Priority.Useful : Priority.Progression,
        [world.Category(name), armCategory, ..count is 1 ? (ReadOnlySpan<Category>)[starterArmCategory] : []],
        count
    );

var settings = world.Category("SETTINGS");
world.Item("TOGGLE COMMENT", Priority.Filler, settings, 7);
var autoShot = world.Item("AUTO SHOT", Priority.Progression, settings);
var stocks = world.Item("PROGRESSIVE STOCKS (+1 LIFE)", null, settings, 8);
var strongestArms = world.Item("LOCK-ON").All | world.Item("CLASSIC").All;
var strongArms = world.Item("GRAVITY").All | strongestArms;

await foreach (var (location, (categoryName, (hintEntrance, (meta, _)))) in Read("InfinityGeneChecks.csv"))
    world.Location(
        location,
        categoryName.Span switch
        {
            "LEVEL 0" => stocks[0],
            "LEVEL 1" => stocks[0],
            "LEVEL 2" => stocks[0],
            "LEVEL 3" => strongArms & stocks[2],
            "EXTRA LEVEL 1" => strongArms & stocks[4],
            "EXTRA LEVEL 2" => strongestArms & stocks[6],
            "EXTRA LEVEL 3" => strongestArms & stocks[8] & autoShot,
            _ => throw new UnreachableException(categoryName.ToString()),
        } &
        (world.AllLocations.TryGetValue(meta, out var inheritedLogic) ? inheritedLogic.Logic : null) &
        world.Item(categoryName, Priority.Progression | Priority.Useful, world.Category("LEVELS")),
        [world.Category(categoryName), world.Category(location)],
        null,
        meta.Span is "VICTORY" ? LocationOptions.Victory : LocationOptions.None,
        hintEntrance: hintEntrance
    );

await world.Game("InfinityGene", "Emik", "EVOLUTION", [world.AllItems["LEVEL 0"], new(starterArmCategory, 1)])
   .DisplayExported(Console.WriteLine)
   .ZipAsync(Path.GetTempPath(), listChecks: true);

Contribute

Issues and pull requests are welcome to help this repository be the best it can be.

License

This repository falls under the MPL-2 license.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 40 6/20/2025