Skip to content

Magic API#3

Open
Carnwennann wants to merge 29 commits intoNenkai:masterfrom
Carnwennann:feature/magic_api
Open

Magic API#3
Carnwennann wants to merge 29 commits intoNenkai:masterfrom
Carnwennann:feature/magic_api

Conversation

@Carnwennann
Copy link
Copy Markdown

Core Magic API

  • Magic API Controller - Published Magic API as a service controller accessible by other mods
  • Magic Entry Reading - Uses FF16Tools.Files.Magic to retrieve complete information about properties, operations, and operation groups from the game's magic system

Magic Writer API

  • MagicWriter Service - New API to write/modify magic files using FF16Tool

Magic Builder Enhancements

  • Add Operation Groups - Added AddOperationGroup() method to dynamically add new operation groups to magic entries
  • Remove Operation Groups - Added RemoveOperationGroup() method to remove existing operation groups from magic entries

Magic Editor UI

  • Cast Magic - Added a "Cast" button on the magic editor to test the effects of magics, it lets you select the source and the target with dropdown menus.
  • Export to Clipboard - Added "Export to Clipboard" button to export the current magic entry configuration as JSON
  • MagicExporter Helper Class - New dedicated class for serializing MagicEntry to JSON format with full structure (OperationGroups → Operations → Properties)

Debugging & Configuration

  • Property Logging - Added configurable property logging for debugging magic API calls
  • Full Property Inspection - Log all properties and injections from the Game API

/// Gets the StaticActorInfo pointer for the player (Clive).
/// </summary>
/// <returns>StaticActorInfo pointer, or nint.Zero if not available.</returns>
nint GetPlayerStaticActorInfo();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't return pointers in APIs, return specific interfaces.

/// The game's targeting system already calculates the correct position.
/// </summary>
/// <returns>A copy of the game's TargetStruct with Type=1 forced, or null if no target.</returns>
TargetStruct? CopyGameTargetStruct();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, don't return structs. Wrap them in interfaces.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay will do.

/// Represents a single modification to a magic spell.
/// This is the public representation of modifications that will be applied.
/// </summary>
public record MagicModification
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be an interface.

/// JSON-serializable format for magic spell modifications.
/// This structure is designed to be human-readable and easy to edit manually.
/// </summary>
public class MagicSpellConfig
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interface.

/// <summary>
/// JSON-serializable format for a single modification.
/// </summary>
public class MagicModificationConfig
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interface.

Comment thread FF16Framework.Interfaces/GameApis/Magic/IMagicBuilder.cs Outdated
/// This enables mods to register their modifications once and have them automatically
/// applied whenever the game loads (or reloads) the magic files.
/// </summary>
public class MagicWriter : IMagicWriter, IDisposable
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a Writer?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MagicWriterAPI (I'm adding the API to it's name) modifies the .magic file in memory using your FF16Tools.Files.Magic edit functions:

  • Hooks into resource loading
  • When a .magic file loads, applies all registered MagicBuilder modifications directly to the parsed file structure
  • Changes happen before any magic execution
  • Zero runtime cost when casting spells

It's the best for permanent modifications to spell behavior.

My idea is to add here the interface to add new MagicIds, the function will the return the ID assigned:
int CreateNewMagicID(string modID, IMagicBuilder builder, string characterId, string magicFileName=Null)

MagicAPI applies modifications at execution time:

  • Intercepts magic casting calls
  • Applies modifications on-the-fly during spell execution
  • Has computational overhead per cast

It's the best for: dynamic modifications that change based on game state (e.g., different VFXs based on combo count, number of projectiles, casting Eid, etc.)

Why both exist:

With MagicWriter: permanent spell tweaks are "baked in" at load time.
With MagicAPI: add situational modifications are applied only when needed.

MagicBuilder is the shared component that defines what modifications to apply to a specific MagicID. Both MagicAPI and MagicWriterAPI consume it.

Comment thread FF16Framework/Extensions.cs Outdated
Comment on lines +46 to +55
public static void AddScan(this IStartupScanner scans, string pattern, Action<nint> action)
{
var baseAddress = Process.GetCurrentProcess().MainModule!.BaseAddress;
scans!.AddMainModuleScan(pattern, result =>
{
if (!result.Found)
throw new Exception($"Scan unable to find pattern: {pattern}!");
action(result.Offset + baseAddress);
});
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check how did you do the Scanning to do the same on my code.

namespace FF16Framework.Services.GameApis.Magic;

/// <summary>
/// Handles magic file processing, property fuzzing, and injection logic.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Fuzzing"?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a term used for automating injections or modification to data used by a piece of software. I will change it to terms like modification and injection.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is frequently used in the context of finding vulnerabilities in software

Comment on lines +82 to +83
_imGui.MenuItemBoolPtr("Property Logging"u8, ""u8, ref _frameworkConfig.GameApis.MagicApi.EnablePropertyLoggingField, true);
_imGui.MenuItemBoolPtr("Injection Logging"u8, ""u8, ref _frameworkConfig.GameApis.MagicApi.EnableInjectionLoggingField, true);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be made clearer, too vague otherwise

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, I will change them to "Log all the properties from casted Magic" and "Log all the injections/modifications done to magic from the MagicAPI"

@Nenkai
Copy link
Copy Markdown
Owner

Nenkai commented Feb 3, 2026

I left a good amount of comments but haven't tested the behavior yet, so consider it just a code quality review for now.

My main issue with the code at the moment besides the reviews are the excessive amount of comments/docs that are pretty vague, I suspect you've used AI here and it has done a pretty poor job conveying the intent

Can you make a quick summary of how the API(s) are meant to be used? Some parts of it seems pretty overengineered and I wonder how this looks like for any consumer.

Furthermore since this is only really intended for FFXVI, service related code should probably go somewhere in Services/Faith/....

@Carnwennann
Copy link
Copy Markdown
Author

I will add here how is the APIs used in tandem from the MagicAPI to the MagicWriterAPI:

// =====================================================
// Simplified example of MagicAPI + MagicWriter usage
// Based on DiaModifications.json for Diara system
// =====================================================

// 1. Create a builder to modify Dia spell (ID=214)
var builder = _magicApi.CreateSpell(magicId: 214);

// 2. (Optional) Import from JSON to avoid steps 3, 4, 5
// builder.ImportFromFile("DiaModifications.json");

// 3. Remove the linear trajectory operation
//    RemoveOperation(operationGroupId, operationId)
builder.RemoveOperation(4338, 1);  // Remove Operation_1 (LinearHomingTrajectory)

// 4. Modify existing properties
//    SetProperty(operationGroupId, operationId, propertyId, value)
builder.SetProperty(4338, 35, 35, 10.0f);  // Duration = 10 seconds

// 5. Add parabolic trajectory operation with properties
//    AddOperation(operationGroupId, operationId, propertyIds, values)
builder.AddOperation(
    operationGroupId: 4338,
    operationId: 2493,                              // ParabolaTrajectory
    propertyIds: new[] { 187, 8, 2430, 2593 },
    values: new object[] 
    { 
        2,                                          // TrajectoryType
        55.0f,                                      // Speed
        new Vector3(-90.0f, 0.0f, 0.0f),           // TrajectoryRotation
        2.0f                                        // TrajectoryCurveStrength
    }
);

// 6. Register to write changes to the .magic file
var handle = _magicWriter.Register(
    modId: "truly.eikonic.spells",
    builder: builder,
    characterId: "c1001"  // Clive
);

// 7. (Optional) Cast the modified Dia spell at runtime
_magicApi.Cast(magicId: 214);

// 8. Unregister when unloading the mod
_magicWriter.Unregister(handle);

I will add examples onto the interfaces.

Carnwennann and others added 17 commits February 4, 2026 23:58
Co-authored-by: Nenkai <Nenkai@users.noreply.github.com>
… added new interfaces to add and remove multiple properties at a time & improved export to JSON functionality to condense the JSON file size
… the entire magicID before applying the modifications
@Carnwennann Carnwennann requested a review from Nenkai February 6, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants