Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/chatmodes/NewEntity.chatmode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
description: "Guided mode to add a new Minecraft entity (and its data) to the Entity Extension using EntityLib metas and Typewriter entry patterns."
tools: ['codebase', 'usages', 'fetch', 'githubRepo', 'editFiles', 'search', 'websearch', 'changes']
---

## purpose

Help contributors add a new vanilla entity to the Typewriter Entity Extension by:

- Identifying the correct EntityLib meta(s) and fields to expose
- Generating Definition/Instance entries and a wrapper that extends `WrapperFakeEntity`
- Reusing existing data entries and appliers only (no new data files)
- Verifying parity with similar entities in the codebase

This mode follows the repo docs in:

- `.github/instructions/entity-extension.instructions.md`
- `.github/instructions/entity-extension.add-entity.instructions.md`

## response style

- Be concise, actionable, and step-driven. Use checklists and short status updates.
- Auto-run read-only lookups (GitHub repo search and local codebase search) without asking permission.
- After 3–5 lookups or when proposing multiple edits, summarize findings and next actions.

## tools

- githubRepo: search Tofaa2/EntityLib for meta classes and fields.
- search/codebase: search this repository for existing entity/data patterns and similar implementations.
- usages: find symbol usages across the codebase to ensure consistent integration.
- fetch/websearch: optional, to reference upstream docs if needed.

## inputs to request (brief)

- Minecraft entity type(s): e.g., PIG, SHEEP, ARMOR_STAND.
- Desired icon (e.g., lucide:/ph:/fa6-solid:) and color (from `Colors`).
- Activity scope: shared or individual (if known).
- Any entity-specific behavior to expose (e.g., “has arms”, “small”, variant).

## workflow

1. Inspect EntityLib metas

- Search https://github.com/Tofaa2/EntityLib/tree/master/api/src/main/java/me/tofaa/entitylib/meta
- Locate `<Entity>Meta` (or applicable meta(s) under `meta/other`, `meta/types`, `meta/projectile`).
- List relevant fields/flags to expose as data (e.g., ArmorStand: small, arms, baseplate, marker, rotations).

2. Audit existing data entries in this repo

- Look under `extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/data/minecraft`:
- Generic: `OnFireData`, `GlowingEffectData`, `PoseData`, `CustomNameData`, `ArmSwingData`, etc. (routed by `GenericData.kt`).
- Living: `living/*` (size, speed, equipment, effects, sleeping, etc.).
- Entity-specific folders: e.g., `living/armorstand/*`.
- If a needed field is missing a data entry, do not create new files. Plan to skip it and document the skip with a small comment in the entity file (see Step 3).

3. Plan file outputs

- Entity file: `entries/entity/minecraft/<Entity>Entity.kt` containing:
- `<Entity>Definition : SimpleEntityDefinition` with `@Entry`, `@Tags`, `@OnlyTags` on `data`.
- `<Entity>Instance : SimpleEntityInstance` (or advanced instance) with `@Entry` and `@OnlyTags`.
- Private runtime wrapper `<Entity>Entity(player)` extending `WrapperFakeEntity(EntityTypes.<TYPE>, player)`.
- No new data files are created in this mode. Use only existing generic/living/entity-specific data entries.
- Add a short comment block near the bottom of the entity file documenting any skipped fields, for example:
```kotlin
// Skipped data (no existing data entry available):
// - ArmorStand: shoulderEntity (not supported — no existing data file)
// - Bee: hasNectar (not supported yet)
```

4. Generate code

- Follow examples like `AllayEntity.kt`, `ArmorStandEntity.kt` for structure.
- In `applyProperty`, route entity-specific properties first, then:
- `if (applyGenericEntityData(entity, property)) return`
- `if (applyLivingEntityData(entity, property)) return`
- Ensure `@OnlyTags` includes: `generic_entity_data`, optionally `living_entity_data`, plus `<entity>_data`.
- When a desired field has no existing data entry, skip implementing it and record it in the "Skipped data" comment block (Step 3). No TODO() or new files.

5. Verify parity & consistency

- Compare to similar entities (living vs non-living vs display).
- Check imports and annotations are consistent (`@Entry`, `@OnlyTags`, `@Tags`, `Var`, `ConstVar`, `Ref`, `emptyRef`).
- Confirm wrappers use EntityLib batching via `WrapperFakeEntity` (no manual viewer/meta batching).

6. Optional integration checks
- If interactions are needed, validate `EntityInteractEventEntry` can target the new definition.
- If the entity participates in objectives, ensure `InteractEntityObjective` can resolve its positions via displays.

## success criteria

- New entity compiles, mirrors structure of peers, and its data properties apply via EntityLib metas using existing data entries only.
- `@Entry` IDs are snake_case and unique; icons/colors consistent.
- Data tags correctly scoped; wrapper routes properties in the right order.
- Skipped fields are briefly documented in a comment block inside the entity file.

## constraints

- Keep edits minimal and localized to the new entity and necessary data files.
- Preserve existing style and patterns; avoid refactoring unrelated code.
- Prefer reusing existing data entries before adding new ones.

## example prompt (user -> this mode)

"Add ARMOR_STAND: use lucide:person-standing (orange). Expose small, arms, baseplate, marker, rotations; shared activity."

The mode should then:

- Find `ArmorStandMeta` in EntityLib; confirm fields.
- Confirm existing data entries in `living/armorstand/*`.
- Generate `ArmorStandDefinition/Instance` and wrapper, with correct `@OnlyTags`.
170 changes: 170 additions & 0 deletions .github/instructions/entity-extension.add-entity.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
applyTo: "extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*"
---

# Entity Extension — Add a new entity (Copilot toolset)

This guide is an actionable checklist for Copilot to add a new Minecraft entity to the Entity Extension. It covers the two main areas you must touch:

- entity classes in `entries/entity/...`
- use existing data properties and appliers in `entries/data/...` (no new data files)

It also references EntityLib (Tofaa2) to discover supported metadata for the entity.

Upstream library: https://github.com/Tofaa2/EntityLib/tree/master/api/src/main/java/me/tofaa/entitylib/meta

## Inputs Copilot needs

- The vanilla entity type name (e.g., ARMOR_STAND, ALLAY, PIG, …) as defined in PacketEvents `EntityTypes`.
- The matching EntityLib meta class (e.g., `ArmorStandMeta`, `LivingEntityMeta`, `PlayerMeta`, …) and any entity-specific meta fields.
- A consistent entry ID prefix (e.g., `armor_stand_definition`, `armor_stand_instance`, tag `armor_stand_data`).
- A short description, an icon id, and a color from `Colors`.

## Step 1: Discover entity metadata in EntityLib

1. Search the EntityLib repo for the meta class:
- Query: `<EntityName>Meta` OR check folders: `meta/other`, `meta/types`, `meta/projectile`, `meta/living`.
- Note important fields and flags you want to expose as Typewriter data (e.g., small/arms/baseplate/marker for ArmorStand).
2. Check generic/living metadata available in EntityLib (e.g., `EntityMeta`, `LivingEntityMeta`) to reuse existing data entries.

## Step 2: Check existing data entries in this repo

In `extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/data/minecraft`:

- Generic data exists: `OnFireData`, `GlowingEffectData`, `PoseData`, `CustomNameData`, `ArmSwingData`, etc. (`GenericData.kt` routes them).
- Living data exists: `living/` folder (size, speed, equipment, potion color, sleeping, etc.).
- Per-entity data often lives under `living/<entity>/` or entity-specific folders (e.g., `living/armorstand/*` for arms, baseplate, marker, small, rotation).

Copilot checklist (reuse-only mode):

- [ ] Use only existing data entries. Do not create new `...Data.kt` or `...Property` files.
- [ ] If a needed field has no existing data entry, skip it and document the skip with a small comment in the entity file (see Step 3).

## Step 3: Create Definition and Instance entries

Definition entry (SimpleEntityDefinition):

- File: `entries/entity/minecraft/<EntityName>Entity.kt`
- Class: `<EntityName>Definition` annotated with `@Entry` and optional `@Tags`.
- Fields: `id`, `name`, `displayName: Var<String>`, `sound: Var<Sound>`, `data: List<Ref<EntityData<*>>>`.
- Constrain `data` with `@OnlyTags("generic_entity_data", "living_entity_data", "<entity>_data")` where applicable.
- `create(player)` returns your private runtime wrapper entity.

Add a short comment block at the bottom of the entity file for any skipped fields:

```kt
// Skipped data (no existing data entry available):
// - ArmorStand: shoulderEntity (not supported — no existing data file)
// - Bee: hasNectar (not supported yet)
```

Instance entry (SimpleEntityInstance or Advanced):

- Class: `<EntityName>Instance` annotated with `@Entry`.
- Fields: `id`, `name`, `definition: Ref<<EntityName>Definition>`, `spawnLocation`, `data` (+ `@OnlyTags`), and `activity: Ref<out SharedEntityActivityEntry>` (or individual).

Follow existing examples like `AllayEntity.kt` and `ArmorStandEntity.kt` for structure, icons, and colors.

## Step 4: Implement the runtime wrapper

- Create a private class `<EntityName>Entity(player: Player) : WrapperFakeEntity(EntityTypes.<TYPE>, player)`.
- Override `applyProperty(property: EntityProperty)`:
1. First handle entity-specific `...Property` types by calling your appliers.
2. Then attempt generic and living:
- `if (applyGenericEntityData(entity, property)) return`
- `if (applyLivingEntityData(entity, property)) return`
- Do not manage viewers/spawn batching; `WrapperFakeEntity` handles metadata batching with EntityLib (`EntityMeta#setNotifyAboutChanges(false/true)`).
- When a desired field has no existing data entry, don’t add new files—just leave it out and keep it listed in the "Skipped data" comment.

## Step 5: Verification and parity checks

Copilot should verify:

- [ ] The new file lives under `entries/entity/minecraft` and compiles alongside peers.
- [ ] `@Entry` IDs are snake_case and unique (e.g., `allay_definition`, `allay_instance`).
- [ ] `@OnlyTags` includes the right tags: `generic_entity_data`, optionally `living_entity_data`, plus `<entity>_data` if that entity-specific set already exists.
- [ ] The wrapper calls `applyGenericEntityData` and `applyLivingEntityData` after entity-specific properties.
- [ ] For living entities, confirm that living data applies; for non-living, only generic applies.
- [ ] Compare to similar entities (e.g., `SheepEntity.kt`, `AllayEntity.kt`) for consistent structure, icons, and descriptions.

Optional runtime checks (when activities/quests interact):

- If interaction support is needed, ensure `EntityInteractEventEntry` filters can target this definition.
- If used in objectives, confirm `InteractEntityObjective` can resolve positions from displays.

## Patterns and helpers to reuse

- Property defaults: `companion object : SinglePropertyCollectorSupplier<Prop>(Prop::class, Prop(default))`.
- Metadata application: `entity.metas { meta<SomeMeta> { /* apply fields */ }; error("Could not apply ... to ${entity.entityType} entity.") }`.
- Smooth rotation/movement in activities: `updateLookDirection`, `Velocity`, update `PositionProperty`.

## Minimal example outline

Skeleton for a new vanilla entity file (abbreviated):

```kt
// ... imports ...

@Entry("allay_definition", "A allay entity", Colors.ORANGE, "ph:flying-saucer-fill")
@Tags("allay_definition")
/**
* The `AllayDefinition` class is an entry that represents an allay entity.
*
* ## How could this be used?
* This could be used to create an allay entity.
*/
class AllayDefinition(
override val id: String = "",
override val name: String = "",
override val displayName: Var<String> = ConstVar(""),
override val sound: Var<Sound> = ConstVar(Sound.EMPTY),
@OnlyTags("generic_entity_data", "living_entity_data", "allay_data")
override val data: List<Ref<EntityData<*>>> = emptyList(),
) : SimpleEntityDefinition {
override fun create(player: Player): FakeEntity = AllayEntity(player)
}

@Entry("allay_instance", "An instance of a allay entity", Colors.YELLOW, "ph:flying-saucer-fill")
/**
* The `Allay Instance` class is an entry that represents an instance of an allay entity.
*
* ## How could this be used?
*
* This could be used to create an allay entity.
*/
class AllayInstance(
override val id: String = "",
override val name: String = "",
override val definition: Ref<AllayDefinition> = emptyRef(),
override val spawnLocation: Position = Position.ORIGIN,
@OnlyTags("generic_entity_data", "living_entity_data", "allay_data")
override val data: List<Ref<EntityData<*>>> = emptyList(),
override val activity: Ref<out SharedEntityActivityEntry> = emptyRef(),
) : SimpleEntityInstance

private class AllayEntity(player: Player) : WrapperFakeEntity(EntityTypes.ALLAY, player) {
override fun applyProperty(property: EntityProperty) {
if (applyGenericEntityData(entity, property)) return
if (applyLivingEntityData(entity, property)) return
}
}
```

## Where to add new data entries

Do not add new data files in this mode. Use only existing entries routed by `applyGenericEntityData` and `applyLivingEntityData`. If a field isn’t available, skip it and document the skip inside the entity file.

## Notes on EntityLib usage

- `WrapperFakeEntity` bridges Typewriter to EntityLib wrappers: it creates IDs/UUIDs via `EntityLib.getPlatform().entityUuidProvider` and `entityIdProvider`, spawns, manages viewers, and batches metadata updates.
- Use meta classes like `EntityMeta`, `LivingEntityMeta`, `ArmorStandMeta`, etc., to set flags/fields via `entity.metas { meta<...> { ... } }`.
- Use only existing shared data entries; if something is missing, skip it and document the skip in the entity file.

## Final sanity checklist (Copilot)

- [ ] New definition and instance files created under `entries/entity/minecraft` with correct annotations and tags.
- [ ] Wrapper entity routes properties and falls back to generic/living appliers.
- [ ] No new data entries were added; only existing data entries are used.
- [ ] Skipped fields are briefly documented in a comment block inside the entity file.
- [ ] Build compiles; types `Ref`, `Var`, `ConstVar`, and `OnlyTags` imports correct.
- [ ] Visual parity: consistent color/icon/description with similar entities.
Loading
Loading