{"id":15036225,"url":"https://github.com/emanzione/spell","last_synced_at":"2025-06-24T21:03:56.726Z","repository":{"id":143022075,"uuid":"398571280","full_name":"emanzione/SPELL","owner":"emanzione","description":"A framework for describing, composing and managing your game's spells and abilities.","archived":false,"fork":false,"pushed_at":"2022-04-19T17:47:06.000Z","size":512,"stargazers_count":44,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-10T05:45:52.532Z","etag":null,"topics":["abilities","csharp","csharp-library","game-development","gamedev","skills","spells","unity","unity3d","unity3d-plugin"],"latest_commit_sha":null,"homepage":"https://mhlab.tech/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emanzione.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["emanzione"],"patreon":"mhlab"}},"created_at":"2021-08-21T13:57:09.000Z","updated_at":"2024-12-28T20:05:57.000Z","dependencies_parsed_at":"2023-04-24T08:36:02.645Z","dependency_job_id":null,"html_url":"https://github.com/emanzione/SPELL","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/emanzione/SPELL","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emanzione%2FSPELL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emanzione%2FSPELL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emanzione%2FSPELL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emanzione%2FSPELL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emanzione","download_url":"https://codeload.github.com/emanzione/SPELL/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emanzione%2FSPELL/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261756526,"owners_count":23205147,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abilities","csharp","csharp-library","game-development","gamedev","skills","spells","unity","unity3d","unity3d-plugin"],"created_at":"2024-09-24T20:30:33.652Z","updated_at":"2025-06-24T21:03:56.700Z","avatar_url":"https://github.com/emanzione.png","language":"C#","funding_links":["https://github.com/sponsors/emanzione","https://patreon.com/mhlab"],"categories":[],"sub_categories":[],"readme":"![SPELL](https://github.com/emanzione/SPELL/blob/main/logo.png)\n\n# SPELL: The Spells Framework\n\n![Build](https://github.com/emanzione/SPELL/workflows/Build/badge.svg)\n[![Nuget](https://img.shields.io/nuget/v/MHLab.Spells)](https://www.nuget.org/packages/MHLab.Spells/)\n\n`SPELL` is a framework for describing, composing and managing your game's spells and abilities.\n\n- __*Component based*__. Costs, requirements, effects are all independent components: you can define and reuse them as you prefer\n- __*Code first*__. Spells and components are defined as C# scripts: no need for external configuration files or different formats, the compiler will ensure the correctness of the spell definition\n- __*Engine agnostic*__. It's just raw C#, it does not depend on any particular game engine\n\nIts core is open source, so you can contribute and freely experiment with it.\n\nBut soon a set of samples and ready-to-use components will be for sale on Unity Asset Store.\n\n## Create your first spell\n\nCreating a new Spell is easy. You just need to implement `ISpellDefinition`:\n\n```csharp\npublic class MySpellDefinition : ISpellDefinition\n{\n    public void SetMetadata(Spell spell)\n    {\n        // Set metadata here: like Cooldown, etc.\n    }\n\n    public void AddRequirements(SpellRequirementContainer container)\n    {\n        // Add requirements for the spell here, like enough mana, selected targets, etc.\n        // If one of the requirements is not met, the spell cast fails.\n        container.Add(new CasterHasEnoughMana(5));\n    }\n\n    public void AddCosts(SpellCostContainer container)\n    {\n        // Add costs for the spell here. Subtract mana from the caster, HP, gold, etc.\n        container.Add(new ManaSpellCost(5));\n    }\n\n    public void AddEffects(SpellEffectContainer container)\n    {\n        // Add the effects here. What happens when the spell is successfully casted? Dealing damage, buffing/debuffing the targets, etc.\n        container.Add\u003cDirectDamage\u003e();\n    }\n}\n```\n\nYou can use the constructor to inject dependencies, if needed.\n\n## Create the context\n\nA `SpellsContext` is like the main container for your spells (and their runtime data). Just create a new instance of `SpellsContext` or inherit from it:\n\n```csharp\npublic class MySpellsContext : SpellsContext\n{\n    public MySpellsContext() : base()\n    {\n        Spells.Add\u003cMySpellDefinition\u003e();\n        // OR (if you need to pass parameters to the constructor):\n        Spells.Add(new MySpellDefinition(myDependency));\n    }\n}\n```\n\nThe context needs to be ticked in your game loop. For example, in Unity it is as simple as:\n\n```csharp\nprivate void Update()\n{\n    _spellsContext.Update(Time.deltaTime);\n}\n```\n\n## Spell Components\n\nSpells are composed by components. In particular: requirements, costs and effects.\n\n### Requirements\n\nYou can create a requirement component by implementing `ISpellRequirement`:\n\n```csharp\npublic class CasterHasEnoughMana : ISpellRequirement\n{\n    private const string RequirementErrorCode = \"CASTER_HAS_NOT_ENOUGH_MANA\";\n    \n    private readonly uint _requiredMana;\n\n    public CasterHasEnoughMana(uint requiredMana)\n    {\n        _requiredMana = requiredMana;\n    }\n    \n    public CheckRequirementResult IsMet(ISpellCaster caster, IEnumerable\u003cISpellTarget\u003e targets, SpellDefinition spellDefinition, SpellsContext context)\n    {\n        var player = (MyPlayer)caster;\n\n        if (player.Mana \u003e= _requiredMana)\n        {\n            return new CheckRequirementResult()\n            {\n                Result = true,\n                Error  = null\n            };\n        }\n\n        return new CheckRequirementResult()\n        {\n            Result = false,\n            Error  = RequirementErrorCode\n        };\n    }\n}\n```\n\n### Costs\n\nYou can create a cost component by implementing `ISpellCost`:\n\n```csharp\npublic class ManaSpellCost : ISpellCost\n{\n    private readonly uint _requiredMana;\n\n    public ManaSpellCost(uint requiredMana)\n    {\n        _requiredMana = requiredMana;\n    }\n    \n    public void ApplyCost(ISpellCaster caster, IEnumerable\u003cISpellTarget\u003e targets, SpellDefinition spellDefinition)\n    {\n        var player = (MyPlayer)caster;\n\n        player.Mana -= _requiredMana;\n    }\n}\n```\n\n### Effects\n\nYou can create an effect component by implementing `ISpellEffect`:\n\n```csharp\npublic class DirectDamage : ISpellEffect\n{    \n    public void Setup(SpellEffectInstance effectInstance)\n    {\n    }\n\n    public SpellEffectContinuationState ApplyEffect(SpellEffectInstance effectInstance, float deltaTime)\n    {\n        var player = (MyPlayer)effectInstance.SpellInstance.Caster;\n\n        foreach (var target in effectInstance.SpellInstance.Targets)\n        {\n            var enemy = (MyTarget)target;\n            enemy.ApplyDamage(player.Damage);\n        }\n\n        return SpellEffectContinuationState.Complete;\n    }\n\n    public void CleanUp(SpellEffectInstance effectInstance)\n    {\n    }\n}\n```\n\n## Casting the spell\n\nDefining your spell is useless if you cannot unleash it against your enemies! The framework offers a way to cast a spell:\n\n```csharp\n\nvar castResult = _spellsContext.CasterSystem.Cast(myPlayer, myTargets, mySpellDefinition, out var spellInstance);\n```\n\nYou can modify some options of the CasterSystem by accessing its Options property:\n\n```csharp\n// Regulates the requirements check. Useful for debugging or cheating. Default = true\n_spellsContext.CasterSystem.Options.CheckRequirements\n// Regulates the costs applying. Useful for debugging or cheating. Default = true\n_spellsContext.CasterSystem.Options.ApplyCosts\n```\n\n## Tests\n\nCheck the [Tests](https://github.com/emanzione/SPELL/tree/main/MHLab.Spells.Tests) folder to see more samples.\n\n## Roadmap\n\n- Considering the feedback from the beta testers\n- Completing the package for the Asset Store. It contains samples and ready-to-use components\n- Optimizing: prepare a load test, profile it, remove runtime allocations, etc\n\n## Contributing\n\nFeel free to open PR or leaving feedback through the Issues. The framework is still in beta, we can shape it together!\n\nIf you want to chat with the community, feel free to join our [Discord server](https://discord.gg/jpteW4yaUN).\n\n## License\n\nThe core of this framework is distributed under the terms of MIT license. You can use it for free, but please: if you adopt it in a commercial project, consider mentioning me (and even buying the full package from the Asset Store! :) ).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femanzione%2Fspell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femanzione%2Fspell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femanzione%2Fspell/lists"}