Skip to content

Latest commit

 

History

History
876 lines (625 loc) · 29 KB

File metadata and controls

876 lines (625 loc) · 29 KB

Ben10Mod Addon API Guide

This guide explains how to build a separate tModLoader addon mod that extends Ben10Mod.

Related docs:

Permission And Distribution Boundaries

You may create and distribute a separate Ben10Mod addon without asking for permission when it is independently written, distributed free of charge, noncommercial, and does not copy or redistribute Ben10Mod code, compiled files, artwork, audio, shaders, documentation, or other assets.

Your published addon must depend on an official Ben10Mod release and require users to obtain Ben10Mod separately. Do not bundle Ben10Mod into the addon or a modpack, and do not present the addon as official or endorsed.

Include a notice substantially similar to:

Unofficial addon for Ben10Mod. Ben10Mod is not included and is separately licensed.

Code blocks expressly presented as examples in the tagged copy of this guide corresponding to the targeted official release may be copied and adapted for a qualifying noncommercial addon. That permission does not extend to implementation code elsewhere in the Ben10Mod repository.

Commercial or monetized addons require prior written permission. This includes sales, paid access or early access, commissions, addon-specific donations or crowdfunding, paid features or support, advertising tied to distribution, and use through a paid server, modpack, hosting plan, or service.

LICENSE.txt contains the controlling legal terms.

Supported Extension Points

Ben10Mod currently exposes these main extension surfaces:

  • Ben10Mod.Content.Items.Accessories.Omnitrix
  • Ben10Mod.Content.Transformations.Transformation
  • Ben10Mod.Content.Transformations.TransformationCostume
  • Ben10Mod.Content.Items.Weapons.PlumbersBadge
  • Ben10Mod.Common.Omnitrix.PermanentTransformationIdentityRegistry

The important design rule is:

  • transformations own alien-specific behavior
  • Omnitrix items own energy, timing, and branch behavior
  • badges stay generic and ask the current transformation what to do

Mental Model

If you are building an addon, this is the model to use:

  • your transformation is a Transformation subclass, not a ModItem
  • your transformation registers itself automatically
  • your transformation becomes active through a ModBuff that sets currentTransformationId
  • your badge attacks are defined on the transformation
  • your Omnitrix item is just another Omnitrix subclass accepted by the shared slot

Permanent Player Identities

Compatibility mods can make a registered transformation act as a player's natural form without adding a timed transformation buff. This is intended for race, species, origin, and character-framework integrations; the core mod does not need to reference any of those systems.

Register one inexpensive per-player provider during your add-on's Load phase and remove it during Unload:

using Ben10Mod.Common.Omnitrix;
using Terraria;
using Terraria.ModLoader;

public sealed class SpeciesIntegrationSystem : ModSystem {
    public override void Load() {
        PermanentTransformationIdentityRegistry.RegisterProvider(Mod, ResolveIdentity);
    }

    public override void Unload() {
        PermanentTransformationIdentityRegistry.UnregisterProviders(Mod);
    }

    private static PermanentTransformationIdentityProfile ResolveIdentity(Player player) {
        return PlayerHasHeatblastSpecies(player)
            ? new PermanentTransformationIdentityProfile(
                "Ben10Mod:HeatBlast",
                energyMax: 300f,
                energyRegen: 2f,
                allowTemporaryOmnitrixTransformations: true)
            : null;
    }
}

The provider is queried every player tick, so it should only read already-loaded player state. Returning null, a blank ID, an unknown transformation, or a blacklisted transformation means that the provider has no applicable identity.

Permanent identity behavior:

  • the transformation's normal stats, movement, rendering, attacks, abilities, and hooks remain the single source of truth
  • no transformation timer buff is added and automatic timeout does not detransform the player
  • the profile supplies a passive energy pool when no Omnitrix is equipped
  • the energy and move HUDs remain available for the permanent form
  • if temporary Omnitrix transformations are allowed, transforming suspends the permanent form and detransforming restores it
  • race-layer visual compatibility is enabled automatically while a permanent identity is present

Providers are evaluated newest-first. Registering another provider from the same owner replaces that owner's previous registration.

Step 1: Add Ben10Mod As A Dependency

You usually want both:

  • a runtime dependency
  • a compile-time project reference

build.txt

Example:

displayName = Ben10 Addon Example
author = YourName
version = 0.1
hideCode = false
hideResources = false
modReferences = Ben10Mod

.csproj

Example:

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="..\\tModLoader.targets" />

  <PropertyGroup>
    <AssemblyName>Ben10Addon</AssemblyName>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\\Ben10Mod\\Ben10Mod.csproj" />
  </ItemGroup>
</Project>

The project reference above is a development-time build dependency. Do not package Ben10Mod's project, assembly, source, or assets inside the published addon. The published mod declares modReferences = Ben10Mod, and users install an official Ben10Mod release separately.

Step 2: Create Your Addon Root

Minimal root file:

using Terraria.ModLoader;

namespace Ben10Addon;

public class Ben10Addon : Mod {
}

Building A Transformation

This is the most important addon type.

What A Transformation Needs

At minimum:

  • a Transformation subclass
  • a ModBuff that sets the active transformation ID

Usually also:

  • icon texture
  • costume or equip content
  • projectiles
  • unlock item or unlock progression hook

Building A Costume

Costumes are lightweight appearance packs that target an existing transformation.

Use a costume when you want to:

  • add an alternate look for a base-mod alien
  • add an alternate look for an addon alien
  • ship palette-capable alternate art without replacing the alien's gameplay

Create a TransformationCostume subclass and set:

  • TargetTransformationId
  • DisplayName
  • optional Description
  • equip texture paths such as HeadTexturePath, BodyTexturePath, LegsTexturePath, BackTexturePath, or other supported equipment-layer paths

For palette support, define PaletteChannels on the costume just like you would on a transformation.

Important behavior:

  • costume selections are saved per player
  • costume selections sync in multiplayer
  • palette data is stored per appearance owner, so the default look and each costume keep separate palette states
  • costume palette channels merge with the target transformation's palette channels by default, with costume channels overriding matching ids

That means an addon can target either:

  • its own transformation, such as Ben10Addon:ShockRock
  • a base Ben10Mod transformation, such as Ben10Mod:HeatBlast

The costume tab in Alien Customization will automatically list any registered costumes for the selected transformation.

Example Costume

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Ben10Mod.Content.Transformations;

namespace Ben10Addon.Content.Transformations.Heatblast;

public sealed class BlueFlareHeatblastCostume : TransformationCostume {
    public override string TargetTransformationId => "Ben10Mod:HeatBlast";
    public override string DisplayName => "Blue Flare";
    public override string Description => "A hotter alternate Heatblast look with its own saved palette.";

    protected override string HeadTexturePath => $"{Mod.Name}/Content/Transformations/Heatblast/BlueFlare_Head";
    protected override string BodyTexturePath => $"{Mod.Name}/Content/Transformations/Heatblast/BlueFlare_Body";
    protected override string LegsTexturePath => $"{Mod.Name}/Content/Transformations/Heatblast/BlueFlare_Legs";

    public override IReadOnlyList<TransformationPaletteChannel> PaletteChannels => new[] {
        new TransformationPaletteChannel(
            id: "flames",
            displayName: "Flames",
            defaultColor: new Color(90, 170, 255),
            overlays: new[] {
                new TransformationPaletteOverlay(
                    $"{Mod.Name}/Content/Transformations/Heatblast/BlueFlare_Body",
                    $"{Mod.Name}/Content/Transformations/Heatblast/BlueFlare_Body_Mask")
            })
    };
}

The same pattern works for:

  • a costume that targets your own transformation
  • a costume pack addon for base Ben10Mod aliens
  • a compatibility addon that targets another addon mod's transformation id

Costume Palette Rules

Costume palettes follow the same UI and storage flow as normal transformation palettes, but the data owner changes.

  • the default appearance stores palette state under the transformation id
  • a selected costume stores palette state under the costume id
  • palette presets are also stored per appearance owner
  • switching between the default look and a costume restores each look's own saved colours and channel toggles

By default, costume channels merge with the target transformation's channels. If you want a costume to define its own full palette layout instead, set:

public override bool MergeTransformationPaletteChannels => false;

Transformation IDs

Use your own mod-prefixed string ID.

Example:

  • Ben10Addon:ShockRock

Do not use Ben10Mod:... for addon content.

Current Action-Slot Model

The public action slots are:

  • primary
  • secondary
  • tertiary
  • ultimate

Each slot can be one of three things:

  • an immediate or timed ability
  • a hold-to-channel ability
  • a badge attack loader

That means F, G, H, and U are not just buff toggles.

Timed Ability Slots

Ability durations, cooldowns, item-use timings, and attack sustain intervals are measured in Terraria ticks. Terraria normally runs at 60 ticks per second, so write a ten-second duration as 10 * 60.

Use properties such as:

  • PrimaryAbilityName
  • PrimaryAbilityDuration
  • PrimaryAbilityCooldown
  • PrimaryAbilityCost
  • SecondaryAbilityName
  • SecondaryAbilityDuration
  • TertiaryAbilityName
  • TertiaryAbilityDuration
  • HasUltimateAbility
  • UltimateAbilityName
  • UltimateAbilityDuration
  • UltimateAbilityCooldown
  • UltimateAbilityCost

Primary, secondary, and tertiary timed abilities are considered present when their duration is greater than zero. The normal timed-ultimate path is explicit: also override HasUltimateAbility => true, because its base value is false.

And optionally:

  • TryActivatePrimaryAbility(...)
  • TryActivateSecondaryAbility(...)
  • TryActivateTertiaryAbility(...)
  • TryActivateUltimateAbility(...)

A fully custom TryActivateUltimateAbility(...) implementation can perform and validate activation itself. Otherwise, use HasUltimateAbility, the ultimate duration, cooldown, and cost properties to use the shared timed-buff path.

Action Input And Live Status

The shared HUD and Codex infer normal timed abilities and Badge loaders automatically. Override GetCombatActionInputKind(...) when a custom action instead runs immediately from an attack-profile slot or must remain held:

public override TransformationActionInputKind GetCombatActionInputKind(
    OmnitrixPlayer.AttackSelection selection, OmnitrixPlayer omp) {
    if (selection == OmnitrixPlayer.AttackSelection.PrimaryAbility)
        return TransformationActionInputKind.Hold;

    return base.GetCombatActionInputKind(selection, omp);
}

This hook controls player-facing input guidance; the transformation must still implement the held behavior itself. For custom state that is not represented by the shared ability buffs, override IsCombatActionActiveForState(...). To explain a prerequisite such as a form resource or required stance, override GetCombatActionBlockReason(...).

Status hooks are queried by the HUD and must be pure: read synchronized state only. Do not spend energy, spawn projectiles, add buffs, print chat, or call an activation method from them.

Badge Attack Slots

Use properties such as:

  • PrimaryAttack
  • SecondaryAttack
  • PrimaryAbilityAttack
  • SecondaryAbilityAttack
  • TertiaryAbilityAttack
  • UltimateAttack

And the matching timing, style, armor penetration, and cost properties.

Important Rule

For a given slot, prefer one mode per state.

If you define both a timed ability and an ability-attack on the same slot, the timed ability wins by default unless you override the activation hook and choose differently yourself.

Attack Naming

The current-attack HUD no longer depends on projectile names.

Use:

  • PrimaryAttackName
  • SecondaryAttackName
  • PrimaryAbilityAttackName
  • SecondaryAbilityAttackName
  • TertiaryAbilityAttackName
  • UltimateAttackName

If a name changes by moveset, use the attack profile DisplayName for that state-specific version.

Timed Ability Naming

The active-ability HUD reads timed ability names from the transformation API.

Use:

  • PrimaryAbilityName
  • SecondaryAbilityName
  • TertiaryAbilityName
  • UltimateAbilityName

If you do not set one, the HUD falls back to the slot label.

Moveset Profiles

Ben10Mod now supports moveset-indexed attack profiles.

Use this when one transformation should swap attack packages by state, for example:

  • powered versus unpowered
  • suit versus unbound
  • normal versus ultimate stance

Current pattern:

  1. override GetMoveSetIndex(OmnitrixPlayer omp)
  2. return one or more TransformationAttackProfile entries from GetPrimaryAttackProfiles() or the equivalent slot method

Each TransformationAttackProfile can define:

  • DisplayName
  • ProjectileType
  • DamageMultiplier
  • UseTime
  • ShootSpeed
  • UseStyle
  • Channel
  • NoMelee
  • ArmorPenetration
  • EnergyCost
  • SustainEnergyCost
  • SustainInterval
  • SingleUse

If you do not need custom movesets, the base implementation already wraps the normal PrimaryAttack, SecondaryAttack, and related properties into a one-entry profile list for you.

Attack Costs

All badge attack profiles support optional energy costs.

Current upfront cost properties:

  • PrimaryEnergyCost
  • SecondaryEnergyCost
  • PrimaryAbilityAttackEnergyCost
  • SecondaryAbilityAttackEnergyCost
  • TertiaryAbilityAttackEnergyCost
  • UltimateEnergyCost

Current sustain cost properties:

  • PrimaryAttackSustainEnergyCost
  • SecondaryAttackSustainEnergyCost
  • PrimaryAbilityAttackSustainEnergyCost
  • SecondaryAbilityAttackSustainEnergyCost
  • TertiaryAbilityAttackSustainEnergyCost
  • UltimateAttackSustainEnergyCost

With matching interval properties:

  • PrimaryAttackSustainInterval
  • SecondaryAttackSustainInterval
  • PrimaryAbilityAttackSustainInterval
  • SecondaryAbilityAttackSustainInterval
  • TertiaryAbilityAttackSustainInterval
  • UltimateAttackSustainInterval

Ben10Mod handles upfront attack spending in the shared badge fire path. Most addon transformations should not manually subtract upfront badge attack energy inside Shoot(...).

Example Transformation

using System.Collections.Generic;
using Ben10Mod;
using Ben10Mod.Content.DamageClasses;
using Ben10Mod.Content.Transformations;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace Ben10Addon.Content.Transformations.ShockRock;

public class ShockRockTransformation : Transformation {
    public override string FullID => "Ben10Addon:ShockRock";
    public override string TransformationName => "Shock Rock";
    public override string Description => "A conductive crystal alien built for ranged pressure and charged badge attacks.";
    public override string IconPath => "Ben10Addon/Content/Interface/ShockRockSelect";
    public override int TransformationBuffId => ModContent.BuffType<ShockRockBuff>();

    public override List<string> Abilities => new() {
        "Crystal bolt primary fire",
        "Charged burst secondary fire",
        "Shield projector action key",
        "Lance storm ultimate"
    };

    public override string PrimaryAttackName => "Crystal Bolt";
    public override string SecondaryAttackName => "Charged Burst";
    public override string PrimaryAbilityAttackName => "Shield Projector";
    public override string UltimateAttackName => "Lance Storm";

    public override int PrimaryAttack => ModContent.ProjectileType<Projectiles.ShockRockBolt>();
    public override int PrimaryAttackSpeed => 16;
    public override int PrimaryShootSpeed => 12;
    public override int PrimaryUseStyle => ItemUseStyleID.Shoot;

    public override int SecondaryAttack => ModContent.ProjectileType<Projectiles.ShockRockBurst>();
    public override int SecondaryAttackSpeed => 28;
    public override int SecondaryShootSpeed => 9;
    public override int SecondaryUseStyle => ItemUseStyleID.Shoot;
    public override int SecondaryEnergyCost => 4;

    public override int PrimaryAbilityAttack => ModContent.ProjectileType<Projectiles.ShockRockShieldNode>();
    public override int PrimaryAbilityAttackSpeed => 22;
    public override int PrimaryAbilityAttackShootSpeed => 0;
    public override int PrimaryAbilityAttackUseStyle => ItemUseStyleID.HoldUp;
    public override int PrimaryAbilityAttackEnergyCost => 8;
    public override bool PrimaryAbilityAttackSingleUse => true;

    public override int UltimateAttack => ModContent.ProjectileType<Projectiles.ShockRockLanceStorm>();
    public override int UltimateAttackSpeed => 20;
    public override int UltimateShootSpeed => 0;
    public override int UltimateUseStyle => ItemUseStyleID.HoldUp;
    public override int UltimateEnergyCost => 35;

    public override int GetMoveSetIndex(OmnitrixPlayer omp) {
        return omp.IsUltimateAbilityActive ? 1 : 0;
    }

    protected override IReadOnlyList<TransformationAttackProfile> GetPrimaryAttackProfiles() {
        return CreateMoveSetProfiles(
            CreatePrimaryAttackProfile(),
            new TransformationAttackProfile {
                DisplayName = "Overcharged Crystal Bolt",
                ProjectileType = ModContent.ProjectileType<Projectiles.ShockRockBolt>(),
                DamageMultiplier = 1.25f,
                UseTime = 12,
                ShootSpeed = 15f,
                UseStyle = ItemUseStyleID.Shoot,
                Channel = false,
                NoMelee = true,
                ArmorPenetration = 4
            }
        );
    }

    public override void UpdateEffects(Player player, OmnitrixPlayer omp) {
        player.GetDamage<HeroDamage>() += 0.08f;
        player.endurance += 0.06f;
    }
}

Example Transformation Buff

using Ben10Mod;
using Terraria;
using Terraria.ModLoader;

namespace Ben10Addon.Content.Transformations.ShockRock;

public class ShockRockBuff : ModBuff {
    public override string Texture => "Ben10Mod/Content/Buffs/Transformations/EmptyTransformation";

    public override void Update(Player player, ref int buffIndex) {
        var omp = player.GetModPlayer<OmnitrixPlayer>();
        omp.currentTransformationId = "Ben10Addon:ShockRock";
        omp.isTransformed = true;
    }

    public override bool RightClick(int buffIndex) => false;
}

If the buff does not set currentTransformationId, the player will not be considered transformed into your form.

Custom Shoot(...) Overrides

You only need to override Shoot(...) if the default projectile spawning is not enough.

Use a custom override when you need:

  • cursor placement
  • projectile spread
  • special sentry limits
  • melee hitboxes
  • transformation-specific branching behavior

Remember:

  • shared attack cost handling already happens before Shoot(...)
  • loaded attack cleanup also happens outside your transformation
  • mouse-targeted and multiplayer-sensitive attacks often need owner-side guards or synced aim

So your custom Shoot(...) usually only needs to worry about the actual attack behavior.

Reading Projectile Combat Context

Ben10Mod attaches synchronized semantic lineage to projectiles created by transformation and badge combat. Addons should read that context instead of identifying an attack from one concrete badge item type or from the legacy itemUsed field. This is descriptive compatibility metadata, not proof that the server authorized a launch.

using Ben10Mod;
using Terraria;

OmnitrixProjectileCombatContext context =
    projectile.GetGlobalProjectile<OmnitrixProjectile>().CombatContext;

if (context.IsHeroAttack) {
    // Apply compatibility behavior intended for transformation combat.
}

The context exposes:

  • OriginatingItemType
  • IsHeroAttack
  • IsPlumbersBadgeAttack
  • CanTriggerHeavenlyCrystalline
  • BlocksOmnitrixEnergyGain

The context is inherited by normal child projectiles created with a projectile parent source and is synchronized through projectile extra AI. Unsupported or malformed payloads cannot upgrade server-established damage or erase context the server already derived.

For a delayed projectile that cannot use the original parent source, copy the context explicitly:

OmnitrixProjectile targetContext =
    delayedProjectile.GetGlobalProjectile<OmnitrixProjectile>();
targetContext.CopyCombatContextFrom(delayedProjectile, sourceProjectile);

ApplyCombatContext(...) is also available when an addon has its own stored semantic context. Both methods normalize item IDs and badge/proc relationships before synchronizing them. Same-owner checks prevent accidental badge, proc, and energy-block inheritance across players; they do not authenticate a client-owned projectile.

Keep these boundaries in mind:

  • treat the context as read-only compatibility information
  • pass an accurate item or parent entity source when spawning custom child projectiles
  • do not infer badge lineage from OriginatingItemType != 0
  • use CopyCombatContextFrom(...) instead of assigning the legacy itemUsed field
  • when a delayed target already has a different server-established item origin, that target evidence wins; use a neutral, player, or correct parent source rather than an unrelated item source
  • do not grant OE, cooldown, movement, damage-class, or other server capabilities from received context alone
  • use an explicit request sequence, server-side source, or independently validated world evidence for server-authoritative addon effects
  • an owner-only spawn check avoids duplicate honest execution; it is not an anti-cheat boundary

Building A Badge

The badge itself is usually simple.

What The Badge Owns

  • base damage
  • rank identity
  • recipe

What The Transformation Owns

  • projectile choice
  • timing
  • style
  • channeling
  • armor penetration
  • attack naming
  • attack energy cost

Example Badge

using Ben10Mod.Content.Items.Weapons;
using Terraria.ID;

namespace Ben10Addon.Content.Items.Weapons;

public class PlumberEliteBadge : PlumbersBadge {
    public override int BaseDamage => 34;
    public override string BadgeRankName => "Elite";
    public override int BadgeRankValue => 7;

    public override void AddRecipes() {
        CreateRecipe()
            .AddIngredient(ItemID.HallowedBar, 12)
            .AddIngredient(ItemID.SoulofLight, 10)
            .AddTile(TileID.MythrilAnvil)
            .Register();
    }
}

Building An Omnitrix

The base Omnitrix class already supports:

  • max energy
  • regen and drain
  • transformation duration rules
  • swap costs
  • damage-to-energy rules
  • evolution rules
  • hand visuals

Example Omnitrix

using Ben10Mod.Content.Items.Accessories;

namespace Ben10Addon.Content.Items.Accessories;

public class TacticalOmnitrix : Omnitrix {
    public override int MaxOmnitrixEnergy => 900;
    public override int OmnitrixEnergyRegen => 5;
    public override int OmnitrixEnergyDrain => 2;
    public override bool UseEnergyForTransformation => true;
    public override int TransformationSwapCost => 60;
}

For real Omnitrix items you will usually also implement:

  • texture loading
  • hand texture registration
  • Clone
  • SaveData
  • LoadData

Unlocking A Transformation

Recommended helper:

using Ben10Mod.Content;

TransformationHandler.AddTransformation(player, "Ben10Addon:ShockRock");

Direct player call also works:

using Ben10Mod;

player.GetModPlayer<OmnitrixPlayer>().UnlockTransformation("Ben10Addon:ShockRock");

Codex Unlock Conditions

Addon transformations can provide codex unlock text in two ways.

Recommended for transformation-owned logic:

public override string GetUnlockConditionText(OmnitrixPlayer omp)
    => "Defeat Shock Rock's boss encounter.";

Or register a condition string through Mod.Call:

ModContent.GetInstance<Ben10Mod>().Call(
    "RegisterTransformationUnlockCondition",
    "Ben10Addon:ShockRock",
    "Defeat Shock Rock's boss encounter.");

Child Transformations And Branching Forms

Use child transformations for:

  • ultimate forms that should behave like full transformations
  • alternate forms
  • branch forms

Important properties:

  • ChildTransformation
  • ChildTransformations
  • ParentTransformation
  • ParentStepDownDelay
  • StepDownToParentOnRepeatedTransform

Use the transform-key hooks when you need custom branching conditions.

Optional: Material Absorption Addon Content

Ben10Mod also exposes absorbable-material registration through Mod.Call.

Current command:

  • RegisterAbsorbableMaterial

This is useful if your addon adds new bars or materials that should work with the Osmosian absorption system.

Runtime Blacklist API

Addons can ask Ben10Mod to hide or disable transformations and major feature groups at runtime.

Transformation blacklist entries support either:

  • a full transformation id such as Ben10Addon:ShockRock
  • a mod id such as Ben10Addon, which blacklists every transformation owned by that mod

Feature blacklist entries currently support:

  • Transformation
  • Omnitrix
  • PlumbersBadge
  • WorldGen

API Commands

Blacklist one or more transformations or whole transformation mods:

ModLoader.GetMod("Ben10Mod")?.Call(
    "BlacklistTransformation",
    "Ben10Addon:ShockRock",
    "SomeOtherAddon");

Blacklist one or more feature groups by mod id:

ModLoader.GetMod("Ben10Mod")?.Call("BlacklistFeature", "Omnitrix", "Ben10Addon");
ModLoader.GetMod("Ben10Mod")?.Call("BlacklistFeature", "PlumbersBadge", "Ben10Addon");
ModLoader.GetMod("Ben10Mod")?.Call("BlacklistFeature", "WorldGen", "Ben10Addon");

You can also use BlacklistFeature with Transformation if you prefer one entry point:

ModLoader.GetMod("Ben10Mod")?.Call("BlacklistFeature", "Transformation", "Ben10Addon");

Query the final effective state, including Ben10Mod's config overrides:

bool transformationsBlocked = (bool)(ModLoader.GetMod("Ben10Mod")?.Call(
    "IsTransformationBlacklisted",
    "Ben10Addon:ShockRock") ?? false);

bool worldGenBlocked = (bool)(ModLoader.GetMod("Ben10Mod")?.Call(
    "IsFeatureBlacklisted",
    "WorldGen",
    "Ben10Addon") ?? false);

Base-Mod Override Config

Ben10Mod now exposes server config toggles that let players keep Ben10Mod-owned content even if an addon blacklists it:

  • AllowBlacklistedBaseTransformations
  • AllowBlacklistedBaseOmnitrixes
  • AllowBlacklistedBasePlumbersBadges
  • AllowBlacklistedBaseWorldGen

That override only applies to content owned by Ben10Mod. Addon-owned content still respects the blacklist normally.

World Generation For Addons

Ben10Mod can automatically enforce transformation, Omnitrix, and badge blacklists for addon content because those features inherit shared base types.

World generation does not have a shared base class, so addon worldgen should check the blacklist explicitly before inserting passes. Use either the Call(...) API above or the public Ben10Mod.Common.Systems.Ben10FeatureBlacklistRegistry helper.

Recommended Checklist Before Shipping An Addon

Make sure:

  • your transformation ID uses your own mod prefix
  • the transformation buff sets currentTransformationId
  • your icon path exists
  • your attacks are defined on the transformation, not hardcoded into the badge
  • your action-slot design matches the current timed-ability versus badge-attack model
  • your attack names are set in the transformation API
  • your moveset-dependent attacks use the profile system
  • your attack costs use the built-in cost properties
  • your mouse-driven attacks are safe in multiplayer
  • your unlock path actually grants the transformation
  • the addon is distributed free of charge and involves no commercial use
  • the addon does not bundle or redistribute Ben10Mod code, binaries, or assets
  • users obtain Ben10Mod separately from an official distribution channel
  • your README or Workshop page includes the required unofficial-addon notice

Practical Tip

If you are not sure how to implement something, use the closest built-in content type as a reference and implement the behavior independently through the public extension API. Do not copy Ben10Mod implementation code. Ben10Mod's extension model is much easier to follow by example than by treating the whole system as a blank-slate framework.