> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/toxicity188/BetterModel/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> Spawn and animate your first BetterModel in 5 minutes

This guide shows you how to load a model, spawn it on an entity, and play an animation.

## Prerequisites

* BetterModel plugin installed on your server
* A `.bbmodel` file placed in `plugins/BetterModel/models/`
* Basic Java/Kotlin knowledge

<Note>
  For this tutorial, we'll assume you have a model file named `demon_knight.bbmodel` with an animation called `attack`.
</Note>

## Step 1: Get a Model

<Steps>
  <Step title="Load the Model">
    Use `BetterModel.model()` to retrieve a model renderer by name. The name corresponds to the `.bbmodel` file without the extension.

    ```java theme={null}
    import kr.toxicity.model.api.BetterModel;
    import kr.toxicity.model.api.data.renderer.ModelRenderer;
    import java.util.Optional;

    Optional<ModelRenderer> renderer = BetterModel.model("demon_knight");
    if (renderer.isEmpty()) {
        plugin.getLogger().warning("Model 'demon_knight' not found!");
        return;
    }
    ```

    <Note>
      Use `BetterModel.modelOrNull()` if you prefer null checks over `Optional`.
    </Note>
  </Step>

  <Step title="Understanding Model Types">
    BetterModel supports two types of models:

    * **General models** (`BetterModel.model()`) - Located in `BetterModel/models/`, used for entities
    * **Player limb models** (`BetterModel.limb()`) - Located in `BetterModel/players/`, used for player cosmetics

    ```java theme={null}
    // For entity models
    Optional<ModelRenderer> entityModel = BetterModel.model("demon_knight");

    // For player models (cosmetics)
    Optional<ModelRenderer> playerModel = BetterModel.limb("steve");
    ```
  </Step>
</Steps>

## Step 2: Spawn the Model

You can spawn models on entities or at specific locations.

### Attach to an Entity

Attach a model to a living entity (e.g., zombie, armor stand):

```java BukkitAdapter.java theme={null}
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.bukkit.platform.BukkitAdapter;
import kr.toxicity.model.api.tracker.EntityTracker;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Zombie;

public void spawnModelOnEntity(Entity entity) {
    EntityTracker tracker = BetterModel.model("demon_knight")
        .map(renderer -> renderer.create(BukkitAdapter.adapt(entity)))
        .orElse(null);
    
    if (tracker != null) {
        plugin.getLogger().info("Model attached to entity!");
    }
}
```

<Note>
  `BukkitAdapter.adapt()` converts Bukkit entities to BetterModel's platform-agnostic entity wrapper.
</Note>

### Spawn at a Location (Dummy)

Create a standalone model at a specific location:

```java theme={null}
import kr.toxicity.model.api.tracker.DummyTracker;
import org.bukkit.Location;

public void spawnModelAtLocation(Location location) {
    DummyTracker tracker = BetterModel.model("demon_knight")
        .map(renderer -> renderer.create(BukkitAdapter.adapt(location)))
        .orElse(null);
    
    if (tracker != null) {
        plugin.getLogger().info("Model spawned at location!");
    }
}
```

### Get or Create Pattern

Use `getOrCreate()` to reuse existing trackers or create a new one:

```java theme={null}
public void getOrCreateModel(Entity entity) {
    EntityTracker tracker = BetterModel.model("demon_knight")
        .map(renderer -> renderer.getOrCreate(BukkitAdapter.adapt(entity)))
        .orElse(null);
    
    // This will return the existing tracker if already attached
}
```

## Step 3: Play an Animation

Once spawned, animate the model using the tracker:

```java theme={null}
import kr.toxicity.model.api.animation.AnimationModifier;

public void playAnimation(EntityTracker tracker) {
    // Play the "attack" animation with default settings
    boolean success = tracker.animate("attack");
    
    if (success) {
        plugin.getLogger().info("Animation started!");
    } else {
        plugin.getLogger().warning("Animation 'attack' not found!");
    }
}
```

### Advanced Animation Control

Control animation speed, looping, and transitions:

```java theme={null}
public void playAdvancedAnimation(EntityTracker tracker) {
    AnimationModifier modifier = AnimationModifier.builder()
        .speed(1.5F)              // 1.5x speed
        .loop(true)                // Loop continuously
        .transition(5)             // 5-tick blend transition
        .removeAfter(false)        // Keep animation after completion
        .build();
    
    tracker.animate("attack", modifier);
}
```

### Stop an Animation

```java theme={null}
public void stopAnimation(EntityTracker tracker) {
    tracker.stopAnimation("attack");
}
```

## Step 4: Customize Display Properties

Update the model's appearance using `TrackerUpdateAction`:

```java theme={null}
import kr.toxicity.model.api.tracker.TrackerUpdateAction;

public void customizeModel(EntityTracker tracker) {
    // Apply red tint
    tracker.update(TrackerUpdateAction.tint(0xFF0000));
    
    // Set brightness (block light: 15, sky light: 15)
    tracker.update(TrackerUpdateAction.brightness(15, 15));
    
    // Enable enchanted glint on all bones
    tracker.update(TrackerUpdateAction.enchant(true));
    
    // Composite multiple updates
    tracker.update(TrackerUpdateAction.composite(
        TrackerUpdateAction.tint(0x00FF00),
        TrackerUpdateAction.glow(true),
        TrackerUpdateAction.glowColor(0xFFFF00)
    ));
}
```

### Apply Updates Before Spawn

You can apply updates before the model is sent to players:

```java theme={null}
public void spawnWithCustomization(Entity entity) {
    EntityTracker tracker = BetterModel.model("demon_knight")
        .map(renderer -> renderer.create(
            BukkitAdapter.adapt(entity),
            TrackerModifier.DEFAULT,
            t -> {
                // Pre-spawn customization
                t.update(TrackerUpdateAction.tint(0x0026FF));
                t.update(TrackerUpdateAction.brightness(15, 15));
            }
        ))
        .orElse(null);
}
```

## Complete Example

Here's a full working example that ties everything together:

```java CompleteExample.java theme={null}
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.animation.AnimationModifier;
import kr.toxicity.model.api.bukkit.platform.BukkitAdapter;
import kr.toxicity.model.api.tracker.EntityTracker;
import kr.toxicity.model.api.tracker.TrackerModifier;
import kr.toxicity.model.api.tracker.TrackerUpdateAction;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

public class ModelCommand implements CommandExecutor {
    
    private final JavaPlugin plugin;
    
    public ModelCommand(JavaPlugin plugin) {
        this.plugin = plugin;
    }
    
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage("Only players can use this command!");
            return true;
        }
        
        Entity target = player.getTargetEntity(5);
        if (target == null) {
            player.sendMessage("Look at an entity!");
            return true;
        }
        
        // Spawn model with customization
        EntityTracker tracker = BetterModel.model("demon_knight")
            .map(renderer -> renderer.create(
                BukkitAdapter.adapt(target),
                TrackerModifier.DEFAULT,
                t -> {
                    // Apply blue tint and full brightness
                    t.update(TrackerUpdateAction.composite(
                        TrackerUpdateAction.tint(0x0026FF),
                        TrackerUpdateAction.brightness(15, 15)
                    ));
                }
            ))
            .orElse(null);
        
        if (tracker == null) {
            player.sendMessage("Failed to spawn model!");
            return true;
        }
        
        // Play attack animation with custom settings
        AnimationModifier modifier = AnimationModifier.builder()
            .speed(1.2F)
            .transition(3)
            .build();
        
        boolean animated = tracker.animate("attack", modifier);
        
        if (animated) {
            player.sendMessage("Model spawned and animated!");
        } else {
            player.sendMessage("Model spawned but animation not found!");
        }
        
        return true;
    }
}
```

## Common Patterns

### Check if Entity Already Has Model

```java theme={null}
import kr.toxicity.model.api.tracker.EntityTrackerRegistry;

public boolean hasModel(Entity entity) {
    return BetterModel.registry(BukkitAdapter.adapt(entity)).isPresent();
}
```

### Remove Model from Entity

```java theme={null}
public void removeModel(EntityTracker tracker) {
    tracker.close(); // Removes all display entities and cleans up
}
```

### Listen for Animation Events

```java theme={null}
import kr.toxicity.model.api.animation.AnimationModifier;

public void playWithCallback(EntityTracker tracker) {
    tracker.animate("attack", AnimationModifier.DEFAULT, () -> {
        // Called when animation completes
        plugin.getLogger().info("Animation finished!");
    });
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Model returns null or empty">
    * Verify the `.bbmodel` file is in `plugins/BetterModel/models/`
    * Check the filename matches (case-sensitive)
    * Look for errors in `plugins/BetterModel/logs/` or server console
    * Ensure the model was loaded on server start
  </Accordion>

  <Accordion title="Animation doesn't play">
    * Confirm the animation name matches exactly (case-sensitive)
    * Check the animation exists in the `.bbmodel` file
    * Verify the tracker is not closed or removed
    * Ensure players are within render distance
  </Accordion>

  <Accordion title="Model not visible">
    * Check that the entity is in a loaded chunk
    * Verify sight tracing settings in `TrackerModifier`
    * Ensure the model has valid textures
    * Try adjusting view range with `TrackerUpdateAction.viewRange()`
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api/bettermodel">
    Explore all available methods and classes
  </Card>

  <Card title="Animation System" icon="film" href="/concepts/animations">
    Deep dive into animation control
  </Card>

  <Card title="Player Models" icon="user" href="/guides/player-models">
    Learn about 12-limb player animations
  </Card>

  <Card title="Examples" icon="book-open" href="/examples/basic-entity-model">
    See real-world usage examples
  </Card>
</CardGroup>
