mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 03:07:34 +00:00
Merge remote-tracking branch 'origin/syntax-highlighter-unified' into syntax-highlighter-unified
This commit is contained in:
commit
b5cff355e6
27 files changed
+1273
-857
No files matched your search
@@ -1,92 +0,0 @@
|
||||
# CNPC+ 1.11 Beta - The Scripting Update
|
||||
|
||||
🟡 **In Testing**
|
||||
|
||||
|
||||
|
||||
## Features: ⚒️
|
||||
|
||||
- **Syntax Highlighting:**
|
||||
|
||||
> Full color-coded script editor. Keywords, types, variables, methods, strings, and comments are all distinct. Scope-aware coloring distinguishes local fields from globals and chained method calls.
|
||||
|
||||
|
||||
|
||||
- **Autocomplete:**
|
||||
|
||||
> Smart suggestions as you type. Context-aware for static vs instance access, shows method parameters, resolves chained calls, and suggests auto imports for classes. Frequently used members prioritized.
|
||||
|
||||
|
||||
|
||||
- **Error Detection:**
|
||||
|
||||
> Catch mistakes before running. Type mismatches, wrong arguments, missing methods, unused imports, duplicate definitions, and more -- all underlined with hover messages explaining the issue.
|
||||
|
||||
|
||||
|
||||
- **Hover Information:**
|
||||
|
||||
> Hover any symbol to see its type, documentation, and declaration. Works on methods, fields, classes, and even generics. Click to pin tooltips in place.
|
||||
|
||||
|
||||
|
||||
- **Script Editor Overhaul:**
|
||||
|
||||
> - **Line Numbers** - Gutter with current line highlighting.
|
||||
> - **Go To Line** - CTRL+G to jump to any line.
|
||||
> - **Search & Replace** - CTRL+F / CTRL+H with real-time highlighting.
|
||||
> - **Go To Definition** - CTRL+Click to jump to any symbol.
|
||||
> - **Smart Undo/Redo** - Word-level instead of character-level.
|
||||
> - **Line Operations** - Duplicate, delete, move up/down.
|
||||
> - **Comment Toggle** - CTRL+/ for selected lines.
|
||||
> - **Rename** - Rename a symbol across all occurrences.
|
||||
> - **Smooth Scrolling** - Eased scroll animation.
|
||||
> - **Indent Guides** - Scope-highlighted vertical indent lines.
|
||||
> - **Brace Matching** - Highlighted pairs, red for unclosed.
|
||||
> - **Smart Brackets** - Auto-pairing for `{}`, `()`, `[]`, `""`, `''`.
|
||||
> - **Fullscreen Mode** - Expand editor to fill the game window.
|
||||
> - **Shortcut Overlay** - View all keyboard shortcuts at a glance.
|
||||
|
||||
|
||||
|
||||
- **Java Scripting (Janino):**
|
||||
|
||||
> Write scripts in real Java compiled at runtime. Name methods after hooks for automatic wiring. Language selector per tab to switch between Java and JavaScript. External `.java` file support.
|
||||
|
||||
|
||||
|
||||
- **Client-Side Scripting:**
|
||||
|
||||
> Run scripts on the client for responsive experiences. Server-controlled -- off by default, must be explicitly enabled. Script files sync automatically on login and reload.
|
||||
|
||||
|
||||
|
||||
- **Cloner Tab Overhaul:**
|
||||
|
||||
> Folder-based organization for saved NPCs, items, and entries. Full-width directory browser for large collections. Improved tab navigation.
|
||||
|
||||
|
||||
|
||||
- **Addon API Support:**
|
||||
|
||||
> Addons can ship type definitions that load into the script editor automatically. Full autocomplete, hover info, and documentation for addon APIs. Addons can extend existing CNPC+ types with new methods.
|
||||
|
||||
|
||||
|
||||
- **Animation Improvements:**
|
||||
|
||||
> Data store for passing data across animation events and frames. New consumer-based task system for animation lifecycle. Expanded scripting API for animation control.
|
||||
|
||||
|
||||
|
||||
**Extras**
|
||||
|
||||
- Player Dialog Events now work like NPC Dialog Events.
|
||||
- Attack speed configuration for linked items.
|
||||
- Client-side balance prediction during Trader interactions.
|
||||
- Right-click cycling on multi-option NPC buttons (Hair, Eyes, Fur, etc.).
|
||||
- Fixed quest cooldown for MC Custom and RL Custom timer types.
|
||||
- Fixed Bard music restarting when opening dialogs.
|
||||
- Fixed NPC biome spawn settings not saving after editing.
|
||||
- Fixed block waypoint issues.
|
||||
- Fixed script config syncing between server and client.
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
# ✏️ CustomNPC+ 1.11 - The Scripting Update ✏️
|
||||
|
||||
---
|
||||
|
||||
The **Scripting Update** completely transforms how you write scripts in CNPC+! A brand new **Script Editor** with **syntax highlighting**, **autocomplete**, and **error detection** makes scripting feel like a real IDE. Write scripts in **Java** with the new Janino engine, enable **client-side scripting** for responsive experiences, and organize your Cloner with **folders**!
|
||||
|
||||
---
|
||||
|
||||
## Script Editor 📝
|
||||
|
||||
The script editor has been rebuilt from the ground up into a **full-featured code editor** right inside Minecraft.
|
||||
|
||||
### Writing Code
|
||||
|
||||
- **Syntax Highlighting** - Keywords, types, variables, methods, strings, and comments are all color-coded automatically. The editor understands your code -- local variables, global fields, and chained method calls each get their own color
|
||||
- **Autocomplete** - Start typing and get smart suggestions for methods, fields, and classes. Suggestions know whether you're in a static or instance context, and frequently used members appear first
|
||||
- **Auto Imports** - Type a class name and the editor suggests the correct import for you
|
||||
- **Error Detection** - Catch mistakes before you even run your script! Wrong argument types, missing methods, type mismatches, unused imports, and more are all underlined with detailed error messages on hover
|
||||
|
||||
> Hover over any symbol to see its **full type information**, documentation, and declaration -- just like a desktop IDE.
|
||||
|
||||
---
|
||||
|
||||
### Navigation
|
||||
|
||||
- **Go To Line** - `CTRL+G` jumps to any line instantly
|
||||
- **Search & Replace** - `CTRL+F` to search, `CTRL+H` to replace. All matches highlight in real-time
|
||||
- **Go To Definition** - `CTRL+Click` any method, field, or type to jump straight to its definition
|
||||
- **Fullscreen Mode** - Expand the editor to fill your entire game window for maximum workspace
|
||||
|
||||
### Editing
|
||||
|
||||
- **Undo/Redo** - Smart undo that works on whole words at a time, not individual characters
|
||||
- **Line Operations** - Duplicate lines with `CTRL+D`, delete lines, and move lines up or down with shortcuts
|
||||
- **Comment Toggle** - `CTRL+/` to comment or uncomment selected lines
|
||||
- **Rename** - Rename a variable or method and every occurrence updates at once
|
||||
- **Copy Whole Line** - Copy with nothing selected grabs the entire line
|
||||
- **Triple Click** - Select an entire line instantly
|
||||
|
||||
### Visual Polish
|
||||
|
||||
- **Line Numbers** - A clean gutter with line numbers and current line highlighting
|
||||
- **Smooth Scrolling** - Eased scroll animations for a polished feel
|
||||
- **Indent Guides** - Vertical lines show your code's nesting depth, with the current scope highlighted in green
|
||||
- **Brace Matching** - Matching `{}` braces light up, and unclosed braces turn red
|
||||
- **Smart Brackets** - Typing `{` automatically creates the closing `}` with correct indentation. Same for `()`, `[]`, `""`, and `''`
|
||||
- **Keyboard Shortcut Overlay** - Press a button to see all available shortcuts at a glance
|
||||
|
||||
---
|
||||
|
||||
## Java Scripting ☕
|
||||
|
||||
Write scripts in **real Java** instead of JavaScript! Powered by the **Janino** compiler, your Java code is compiled and runs natively.
|
||||
|
||||
- **Full Java Language** - Use classes, interfaces, enums, generics, lambdas, and everything you know from Java
|
||||
- **Automatic Hook Resolution** - Just name your methods after the hook you want (e.g., `init`, `tick`, `interact`) and they're wired up automatically
|
||||
- **Language Selector** - Each script tab has a dropdown to choose between JavaScript and Java. Mix and match across tabs
|
||||
- **External Files** - Write `.java` scripts in an external editor and they're loaded automatically
|
||||
|
||||
> Java and JavaScript scripts coexist side by side. Pick whichever language fits your workflow!
|
||||
|
||||
---
|
||||
|
||||
## Client-Side Scripting 🖥️
|
||||
|
||||
Scripts can now run **on the client** for responsive, visual experiences!
|
||||
|
||||
- **Server Controlled** - Server owners decide whether client scripting is allowed. Players can't enable it on their own
|
||||
- **Automatic Sync** - Script files are sent from the server to all connected clients on login, and re-synced whenever scripts are reloaded
|
||||
- **Safe by Default** - Client scripting is off by default and must be explicitly enabled in the server config
|
||||
|
||||
> Client-side scripts open the door to custom UI effects, visual feedback, and client-only hooks -- all while the server stays in full control.
|
||||
|
||||
---
|
||||
|
||||
## Cloner Tab Overhaul 📂
|
||||
|
||||
The Cloner now supports **folders** for organizing your saved NPCs, items, and entries!
|
||||
|
||||
- **Folder System** - Create folders and subfolders to keep your cloner organized however you like
|
||||
- **Full Screen Browser** - A new full-width directory view makes browsing large collections much easier
|
||||
- **Improved Tabs** - Better tab navigation for switching between cloner categories
|
||||
|
||||
---
|
||||
|
||||
## Addon API Support 📘
|
||||
|
||||
Addon developers can now ship **type definitions** alongside their mods, giving scripters full autocomplete and documentation for addon APIs!
|
||||
|
||||
- **Automatic Loading** - Any mod can include API definitions and they'll appear in the script editor automatically
|
||||
- **Patch Support** - Addons can extend existing CNPC+ types with new methods (e.g., a DBC addon adding `getDBCPlayer()` to the Player type)
|
||||
- **Full Documentation** - Parameter names, return types, and descriptions all show up in autocomplete and hover info
|
||||
|
||||
> If you're a scripter using addons, you get autocomplete for their APIs with zero setup. If you're an addon developer, just include your definitions and everything works.
|
||||
|
||||
---
|
||||
|
||||
## Animation Improvements 🎬
|
||||
|
||||
- **Data Store** - Animations can now store and pass data across events and frames for more complex animation logic
|
||||
- **Task System** - A new consumer-based system for reacting to animation lifecycle events, replacing the old approach
|
||||
- **Expanded API** - More animation control exposed to the scripting API
|
||||
|
||||
---
|
||||
|
||||
## Additional Changes 🔧
|
||||
|
||||
- **Player Dialog Events** - Now work the same way as NPC Dialog Events for consistency
|
||||
- **Linked Item Attack Speed** - Added attack speed configuration to linked items
|
||||
- **Trader Balance Preview** - Client-side balance prediction shows your expected balance during trades
|
||||
- **Right-Click Cycling** - Multi-option NPC buttons (Hair, Eyes, Fur, etc.) can now be right-clicked to cycle backwards
|
||||
- **Quest Cooldown Fix** - Fixed cooldown not working for MC Custom and RL Custom timer types
|
||||
- **Bard Music Fix** - Fixed music restarting or duplicating when opening dialogs
|
||||
- **Biome Spawn Fix** - Fixed NPC biome spawn settings not saving after editing
|
||||
- **Block Waypoint Fix** - Fixed issues with block waypoints
|
||||
- **Script Config Fix** - Fixed script configuration not syncing properly between server and client
|
||||
|
||||
---
|
||||
@@ -1,368 +0,0 @@
|
||||
# ⚔️ CustomNPC+ 1.11 - Abilities ⚔️
|
||||
|
||||
---
|
||||
|
||||
The **Ability Update** introduces a full combat ability system to CNPC+! Give your NPCs powerful attacks with **visual telegraphs**, launch **energy projectiles** that home in on targets, set **traps** and **hazards**, chain abilities into devastating combos, and let players unlock abilities of their own!
|
||||
|
||||
---
|
||||
|
||||
## How Abilities Work 🎯
|
||||
|
||||
Every ability follows a **phase-based execution flow** that gives combat a readable, strategic feel.
|
||||
|
||||
**Phases:**
|
||||
- **Wind-up** - The ability charges. Telegraphs appear, animations play, and observant players can react
|
||||
- **Active** - The ability fires. Damage is dealt, projectiles launch, effects apply
|
||||
- **Dazed** - Optional recovery state if the ability is interrupted during wind-up
|
||||
|
||||
**Core Configuration:**
|
||||
- **Cooldown** - Time before the ability can be used again. Supports both a **universal cooldown** and **per-ability cooldowns**
|
||||
- **Targeting Mode** - Aggro Target, Self, AOE on Self, or AOE on Target
|
||||
- **Range** - Configurable min and max range for activation
|
||||
- **Lock Movement** - Lock the caster's movement, rotation, or both during any phase
|
||||
- **Interruptible** - Allow damage to interrupt wind-up and force a dazed state
|
||||
- **Weight** - Priority weighting for NPC random ability selection
|
||||
- **Invulnerability** - Make the caster invulnerable during Wind-up, Active, or both phases
|
||||
- **Ignore I-Frames** - Bypass Minecraft's damage immunity timer, allowing rapid consecutive hits
|
||||
- **Free on Cast** - The ability enters cooldown the moment it launches, freeing the caster while spawned entities (projectiles, zones, barriers) persist independently
|
||||
- **Magic Damage** - Abilities can deal magic damage as a separate damage type, with configurable damage values and split ratios
|
||||
|
||||
> Abilities support both **inline configuration** on individual NPCs and **global presets** that update everywhere when edited.
|
||||
|
||||
---
|
||||
|
||||
## Melee Abilities 🗡️
|
||||
|
||||
Close-range attacks that hit hard and punish positioning.
|
||||
|
||||
- **Slam** - Leap toward the target in an arc, dealing AOE damage on impact. Configurable radius, knockback, and leap height. Air slams deal reduced damage based on required leap height
|
||||
- **Heavy Hit** - A single powerful melee strike with bonus knockback and a configurable hit delay
|
||||
- **Cutter** - A slashing arc attack in a cone. Configurable angle and reach
|
||||
|
||||
> All melee abilities support configurable **damage**, **knockback**, and **potion effects** on hit.
|
||||
|
||||
---
|
||||
|
||||
## Energy Projectiles ✨
|
||||
|
||||
A brand new projectile system built from the ground up with full visual customization. Energy projectiles use a **Variant System** — each base type (Orb, Beam, Disc) can be configured with variants like dual-fire or barrage patterns, all from within the same ability type.
|
||||
|
||||
### Orbs
|
||||
|
||||
Homing energy spheres that track their target through the air. Configurable with variants for dual or barrage firing patterns.
|
||||
|
||||
### Discs
|
||||
|
||||
Flat spinning projectiles with optional **boomerang** behavior. Supports both vertical and horizontal orientation.
|
||||
|
||||
### Beams
|
||||
|
||||
Sustained energy beams that connect the caster to their target. Supports a **Free Aim** mode for manual aiming without homing.
|
||||
|
||||
### Laser Shot
|
||||
|
||||
An **instant-fire** hitscan projectile with configurable width and speed. No homing — pure aim.
|
||||
|
||||
### Energy Configuration
|
||||
|
||||
Every energy projectile shares a **modular data system** for deep customization:
|
||||
|
||||
- **Anchor Point** - Choose where the projectile spawns: Right Hand, Left Hand, Front, Center, Chest, or Above Head. Supports custom offset anchors and a **Launch From Anchor** setting
|
||||
- **Display** - Set core color, edge color, glow effect, transparency, and size
|
||||
- **Homing** - Toggle homing, adjust turn rate and max tracking range
|
||||
- **Combat** - Damage, knockback, vertical knockback, hit cooldown, and optional AOE splash
|
||||
- **Hit Type** - Choose how the projectile interacts with targets: **Single** (hits one), **Pierce** (passes through), or **Multi** (hits multiple)
|
||||
- **Lifespan** - Projectile speed, max age, and max travel distance (up to 150 blocks)
|
||||
- **Explosions** - Energy projectiles can detonate on impact with configurable explosion radius. Block damage can be toggled in the Energy Config
|
||||
|
||||
---
|
||||
|
||||
## Energy Barriers 🛡️
|
||||
|
||||
Defensive energy constructs that block incoming projectiles and attacks.
|
||||
|
||||
- **Dome** - A spherical barrier centered on the caster. Blocks incoming energy projectiles with configurable per-type damage multipliers, HP/durability, and duration
|
||||
- **Wall** - A placeable flat energy panel with configurable dimensions and height offset. Has an optional **launch mode** that fires the wall forward, dealing damage and knockback
|
||||
- **Shield** - A held energy panel that follows the caster's facing direction for directional blocking
|
||||
|
||||
**Barrier Properties:**
|
||||
- **HP & Durability** - Barriers have health and break when depleted
|
||||
- **Melee Vulnerability** - Barriers can be configured to take damage from melee attacks
|
||||
- **Damage Absorption** - Attacks aimed at the caster can be redirected to damage the barrier instead
|
||||
- **Ally/Target Filtering** - Configure which projectiles are blocked based on ownership
|
||||
- **Offset Support** - Position barriers with custom offsets from the caster
|
||||
|
||||
---
|
||||
|
||||
## Defensive Abilities 🏰
|
||||
|
||||
Reactive defensive stances that protect the caster when hit.
|
||||
|
||||
- **Guard** - Enter a defensive stance that **reduces incoming damage** by a configurable percentage. Lasts until duration expires or a max hit count is reached
|
||||
- **Counter** - Absorb incoming attacks and **counter-attack the attacker**. Counter damage can be a flat value or a percentage of the absorbed damage. Plays a counter-attack animation on each successful counter
|
||||
- **Dodge** - Fully **evade incoming attacks**, negating all damage. Plays randomized dodge animations from up to three configurable animation slots
|
||||
|
||||
> All defensive abilities support configurable duration and maximum hit counts before they break.
|
||||
|
||||
---
|
||||
|
||||
## Movement Abilities 💨
|
||||
|
||||
Abilities that reposition the caster in combat.
|
||||
|
||||
- **Charge** - Rush forward in a line, damaging all entities in the path. Configurable speed, hit width, and knockback
|
||||
- **Dash** - A quick directional movement without dealing damage
|
||||
- **Teleport** - Instantly relocate to the target's position
|
||||
|
||||
> **Charge** locks direction during wind-up, so the telegraph accurately shows the attack path.
|
||||
|
||||
---
|
||||
|
||||
## AOE Abilities 🌀
|
||||
|
||||
Area control abilities that dominate space.
|
||||
|
||||
- **Shockwave** - An AOE burst radiating outward from the caster, dealing damage and knockback in a configurable radius
|
||||
- **Sweeper** - A wide rotational sweep attack around the caster with configurable radius and rotation speed. Can run concurrently with other abilities in chains
|
||||
- **Vortex** - Creates a pulling force that drags nearby entities toward the center while dealing damage
|
||||
|
||||
---
|
||||
|
||||
## Hazards & Traps 🪤
|
||||
|
||||
Ground-based zone abilities for area denial and ambush tactics.
|
||||
|
||||
### Hazards
|
||||
|
||||
Persistent damaging areas that continuously hurt entities standing inside them. Configurable damage interval, zone count (up to 20), shape (circle or square), zone height, and duration. Can optionally affect the caster.
|
||||
|
||||
**Zone Visual Presets:** Default, Toxic, Inferno, Arcane, Electric, Frost — each with themed colors, particles, and visual effects.
|
||||
|
||||
### Traps
|
||||
|
||||
Proximity-triggered burst abilities that sit dormant until an entity enters the trigger radius. Support configurable arm time, multiple triggers before expiring, trigger cooldown, damage radius, and knockback. Traps can be **hidden** (invisible) until triggered, revealing with a brief burst effect.
|
||||
|
||||
**Trap Presets:**
|
||||
- **Hidden** - Minimal visibility, no effects — a pure stealth trap
|
||||
- **Venom** - Applies Poison with toxic green visuals
|
||||
- **Explosive** - Applies Fire with fiery orange particles
|
||||
- **Cursed** - Applies Weakness with purple arcane effects
|
||||
- **Shock** - Applies Mining Fatigue with blue electrical sparks
|
||||
- **Snare** - Applies Slowness with icy blue visuals
|
||||
|
||||
### Zone Visuals
|
||||
|
||||
Both hazards and traps share a rich visual system with configurable layers: ground fill, concentric rings, rotating borders, accents (static, swaying, or flickering), lightning arcs, and particle effects with multiple motion types (rising, drifting, sparks).
|
||||
|
||||
---
|
||||
|
||||
## Ability Effect 💫
|
||||
|
||||
A support ability for applying buffs, debuffs, healing, and custom effects to allies or enemies.
|
||||
|
||||
**Targeting:**
|
||||
- **Self** - Affect only the caster (self-buffs, self-healing)
|
||||
- **AOE Self** - Area-of-effect centered on the caster with a configurable radius
|
||||
|
||||
**Target Filter (AOE):**
|
||||
- **Allies** - Only affect friendly entities
|
||||
- **Enemies** - Only affect hostile entities
|
||||
- **All** - Affect everyone in range
|
||||
|
||||
Allies are determined by **Party** for players and **Faction** for NPCs. A toggle controls whether the caster is included in AOE effects.
|
||||
|
||||
**Effect Types:**
|
||||
- Flat and percentage-based healing (instant or over time)
|
||||
- Vanilla potion effects
|
||||
- Custom effects from the Custom Effect system
|
||||
- Mod-registered effect actions
|
||||
|
||||
**Built-in Variants:** Self Heal, Healing Aura, Poison Debuff.
|
||||
|
||||
---
|
||||
|
||||
## Custom Ability 🔧
|
||||
|
||||
A blank-slate ability type designed for **scripters** to build fully custom abilities through script hooks.
|
||||
|
||||
**Script Hooks:**
|
||||
- **Start** - Fired when the ability begins
|
||||
- **Execute** - Fired when entering the active phase
|
||||
- **Tick** - Fired every game tick with phase and tick count
|
||||
- **Complete** - Fired when the ability finishes
|
||||
- **Toggle / Toggle Tick** - For toggle-mode abilities with multiple states
|
||||
|
||||
**Features:**
|
||||
- Full telegraph control with all shape types (Circle, Ring, Line, Cone, Point, Square)
|
||||
- Configurable telegraph dimensions, colors, and duration
|
||||
- Supports both instant execution and multi-state toggle modes
|
||||
|
||||
---
|
||||
|
||||
## Burst System 🔁
|
||||
|
||||
Abilities can be configured to **fire multiple times in rapid succession** as a single use.
|
||||
|
||||
- **Burst Amount** - Number of additional repetitions after the initial cast
|
||||
- **Burst Delay** - Ticks between each burst iteration
|
||||
- **Replay Animations** - Optionally replay the wind-up animation on each burst
|
||||
- **Overlap Mode** - Previous entities can persist while the next burst fires, allowing stacking effects
|
||||
|
||||
> Burst is available on most ability types including energy projectiles, melee abilities, and movement abilities.
|
||||
|
||||
---
|
||||
|
||||
## Chained Abilities ⛓️
|
||||
|
||||
Chain multiple abilities together into **sequential combos** that execute one after another.
|
||||
|
||||
- **Ordered Entries** - Each entry in the chain references an ability with a configurable delay before it fires
|
||||
- **Wind-up All** - Optionally wind up all abilities simultaneously before the chain begins
|
||||
- **Chain Cooldown** - A single cooldown applied after the entire chain completes
|
||||
- **Chain Conditions** - Conditions checked once for the whole chain, not per-ability
|
||||
|
||||
### Concurrency
|
||||
|
||||
Specific ability types can be marked to run **concurrently** within a chain — executing alongside the primary ability instead of waiting in sequence. Concurrent-capable types include Effect, Hazard, Trap, and Sweeper.
|
||||
|
||||
> Chained abilities work with the ability hotbar and support live previewing in the editor.
|
||||
|
||||
---
|
||||
|
||||
## Telegraph System 📍
|
||||
|
||||
**Telegraphs** are visual ground indicators that warn players before an ability lands. They appear during the wind-up phase and transition into a warning state before the ability fires.
|
||||
|
||||
**Shapes:**
|
||||
- **Circle** - Filled circle with configurable radius
|
||||
- **Ring** - Donut shape with inner and outer radius
|
||||
- **Line** - Rectangle along the attack direction
|
||||
- **Cone** - Fan/wedge shape with configurable angle
|
||||
- **Point** - Single point indicator
|
||||
- **Square** - Square area indicator
|
||||
|
||||
**Features:**
|
||||
- **Color Customization** - Set ARGB colors for both the telegraph and its warning phase
|
||||
- **Warning Phase** - Color shifts in the final ticks before the ability fires
|
||||
- **Entity Tracking** - Telegraphs can follow a moving entity or stay at a fixed position
|
||||
- **Height Handling** - Telegraphs properly handle ground and air positioning
|
||||
- **Custom Controls** - Full telegraph configuration available on Custom Abilities
|
||||
|
||||
> Telegraphs are saved as **reusable presets** and can be shared across multiple abilities.
|
||||
|
||||
---
|
||||
|
||||
## Animations & Sound 🎬
|
||||
|
||||
Abilities integrate directly with the animation and sound systems.
|
||||
|
||||
- **Wind-up Animation** - Plays during the charging phase
|
||||
- **Active Animation** - Plays when the ability fires
|
||||
- **Dazed Animation** - Plays during the dazed recovery state
|
||||
- **Animation Sync** - Optionally sync wind-up duration to match animation length exactly
|
||||
- **Sound Events** - Assign custom sounds to both wind-up and active phases
|
||||
- **Phase Colors** - Visual color feedback on the caster during wind-up and active
|
||||
- **Multiple Animations** - Multiple animations can be registered under one common name
|
||||
|
||||
> Many ability types include **built-in animation names** that work automatically when matching animations exist.
|
||||
|
||||
---
|
||||
|
||||
## Effects & Conditions ⚡
|
||||
|
||||
### Potion Effects
|
||||
|
||||
Abilities can apply **potion effects** to any entity they hit. Each effect has configurable type, duration, and amplifier. Supports both preset effect types and **manual potion IDs** for custom or modded effects with amplification up to 255.
|
||||
|
||||
**Available Preset Effects:**
|
||||
Fire, Slowness, Weakness, Poison, Wither, Blindness, Nausea, Hunger, Mining Fatigue
|
||||
|
||||
### Conditions
|
||||
|
||||
Abilities support a **condition system** that controls when they can be used. Each condition can be checked on the **Caster**, the **Target**, or **Both**.
|
||||
|
||||
**Condition Types:**
|
||||
- **HP Threshold** - Check if an entity's health is above, below, or equal to a value (supports flat or percentage)
|
||||
- **Hit Count** - Require the caster to have been hit a certain number of times within a time window
|
||||
- **Has Item** - Check if an entity is holding, wearing, or carrying a specific item
|
||||
- **Quest Completed** - Check if a player has completed a specific quest
|
||||
- **Has Effect** - Check if an entity has a specific custom effect applied
|
||||
|
||||
---
|
||||
|
||||
## Player Abilities 🧑
|
||||
|
||||
Players can unlock and use abilities with a full hotbar and management system!
|
||||
|
||||
### Ability Hotbar
|
||||
|
||||
The **Ability Hotbar** is a 12-slot bar for equipping and switching between unlocked abilities. Hold the **HUD Key** and use the **scroll wheel** to cycle through your equipped abilities. The hotbar displays ability icons with cooldown progress and supports horizontal or vertical layouts with 3, 5, or 7 visible slots.
|
||||
|
||||
Configure your hotbar through drag-and-drop in the **CNPC+ Inventory Ability Tab**, and customize its appearance in the **HUD Editor** under CNPC+ Inventory Settings.
|
||||
|
||||
### Player Features
|
||||
- **Unlock System** - Players receive abilities via scripting or commands
|
||||
- **Per-Ability Cooldowns** - Each ability can have its own cooldown in addition to the universal cooldown
|
||||
- **Cancel on Double-Tap** - Double-tap the ability key during wind-up to cancel and immediately enter cooldown
|
||||
- **Ability Visibility** - Hide or show specific abilities in the HUD and usage
|
||||
- **State Management** - Abilities are properly cancelled on logout, dimension change, death, and respawn. Movement input is suppressed during ability locks
|
||||
- **Flight Safety** - Abilities do not affect players who are flying
|
||||
|
||||
---
|
||||
|
||||
## Scripting API 📜
|
||||
|
||||
Full scripting support for ability lifecycle events:
|
||||
|
||||
- **StartEvent** *(Cancelable)* - Fired when wind-up begins
|
||||
- **ExecuteEvent** *(Cancelable)* - Fired when the ability activates
|
||||
- **InterruptEvent** - Fired when wind-up is interrupted by damage
|
||||
- **CompleteEvent** - Fired when the ability finishes
|
||||
- **HitEvent** *(Cancelable)* - Fired on each entity hit. Damage and knockback values can be modified
|
||||
- **TickEvent** - Fired every tick with current phase and tick count
|
||||
- **PlayerAbilityEvent** - Events for player ability actions
|
||||
|
||||
### Energy Projectile Scripting
|
||||
|
||||
Full scripting interfaces for all energy projectile types with 5 event hooks:
|
||||
- **Fired** - When the projectile launches
|
||||
- **Update** - Every tick during flight
|
||||
- **EntityImpact** - On hitting an entity
|
||||
- **BlockImpact** - On hitting a block
|
||||
- **Expired** - When the projectile's lifespan ends
|
||||
|
||||
### Ability Extender System
|
||||
|
||||
Register custom **lifecycle hooks** on abilities with the Ability Extender system:
|
||||
- **onAbilityStart** - When the ability begins
|
||||
- **onAbilityTick** - Every tick during execution
|
||||
- **onAbilityComplete** - When the ability finishes
|
||||
- **onAbilityDamage** - When the ability deals damage
|
||||
|
||||
Multiple extenders can be registered per ability. Ability scripts are **instanced** — every use gets its own fresh set of variables for optimal performance.
|
||||
|
||||
> Scripts can cancel, modify damage, and react to every phase of ability execution.
|
||||
|
||||
---
|
||||
|
||||
## Ability Preview 🔎
|
||||
|
||||
The ability editor includes a **live preview system**. Test your telegraph shapes, animations, projectile visuals, burst patterns, and chained ability sequences directly in the GUI on a dummy NPC — no world effects applied.
|
||||
|
||||
---
|
||||
|
||||
## Ability Commands 💬
|
||||
|
||||
- `/kam ability list` - List all custom abilities
|
||||
- `/kam ability types` - List all registered ability types
|
||||
- `/kam ability prebuilts` - List all built-in abilities
|
||||
- `/kam ability info <name>` - View ability details
|
||||
- `/kam ability delete <name>` - Delete a custom ability
|
||||
- `/kam ability reload` - Reload abilities from disk
|
||||
- `/kam ability give` - Give an ability to a player
|
||||
- `/kam ability remove` - Remove an ability from a player
|
||||
- `/kam ability giveChain` - Give a chained ability to a player
|
||||
- `/kam ability removeChain` - Remove a chained ability from a player
|
||||
- `/kam ability player` - Manage player abilities
|
||||
|
||||
---
|
||||
@@ -1,167 +0,0 @@
|
||||
# 🏪 CustomNPC+ 1.11 - Market Update 🏪
|
||||
|
||||
---
|
||||
|
||||
The **Market Update** brings a complete economy system to CNPC+! Create bustling marketplaces with **Traders** that have limited stock, or let players trade with each other through the brand new **Auction House**!
|
||||
|
||||
---
|
||||
|
||||
## Currency System 💰
|
||||
|
||||
CNPC+ now has a **built-in currency** used across all Market features!
|
||||
|
||||
- **Fully Configurable** - Name it Coins, Gold, Credits, whatever fits your world. Set a starting balance and max balance cap to shape your economy
|
||||
- **Profile Persistent** - Currency carries across all your character slots
|
||||
- **Vault Support** - Integrates seamlessly with Bukkit-compatible economy plugins if Vault is installed. Falls back to built-in currency otherwise
|
||||
- **Lifetime Tracking** - Tracks total earned and spent per player
|
||||
|
||||
> Full Scripting API support for currency operations on players.
|
||||
|
||||
---
|
||||
|
||||
## Auction House 🏛️
|
||||
|
||||
A **global player marketplace** where players can list items, bid against each other, and purchase instantly!
|
||||
|
||||
### Selling
|
||||
|
||||
List any item with a **Starting Price** for competitive bidding. Set an optional **Buyout Price** for players who want to skip the wait and buy immediately. Auctions last **24 hours** by default with a configurable **minimum listing price**.
|
||||
|
||||
> A configurable **Listing Fee** is charged when creating auctions, and a **Sales Tax** is taken from successful sales.
|
||||
|
||||
### Bidding
|
||||
|
||||
Compete for items by placing bids! Each bid must be at least **5%** higher than the current bid. **Snipe Protection** extends auctions when last-second bids come in, giving everyone a fair shot. If you get outbid, your currency is **automatically refunded**.
|
||||
|
||||
### Buyout
|
||||
|
||||
See something you need right now? Hit **Buy Now** to purchase instantly at the seller's Buyout Price. If you're currently the highest bidder on an item, you only pay the **difference** between your bid and the buyout.
|
||||
|
||||
### My Trades
|
||||
|
||||
One screen to manage all your auction activity:
|
||||
|
||||
- **Active Listings** - Items you're currently selling
|
||||
- **Active Bids** - Auctions you're competing in
|
||||
- **Ready to Claim** - Won items and currency waiting for pickup
|
||||
|
||||
Players can **cancel** their own listings — items are returned as a claim. If there are active bids on the listing, a configurable **cancellation penalty** is charged (default 10%).
|
||||
|
||||
> Claims expire after **20 days**. Make sure to collect your items and currency before they're gone!
|
||||
|
||||
### Search & Sorting
|
||||
|
||||
Find what you're looking for with search and sort tools. Search filters by both **item name** and **seller name** simultaneously.
|
||||
|
||||
**Sort Options:**
|
||||
- Newest
|
||||
- Ending Soon
|
||||
- Price (Low to High)
|
||||
- Price (High to Low)
|
||||
- Most Bids
|
||||
|
||||
### Notifications
|
||||
|
||||
Get notified immediately when:
|
||||
- Your item **sells**
|
||||
- You get **outbid**
|
||||
- You **win** an auction
|
||||
- Your auction **expires** with no bids
|
||||
- A **claim is ready** for pickup
|
||||
|
||||
Offline? You'll get a full **claim summary** when you log back in showing exactly what's waiting for you.
|
||||
|
||||
### Trade Slots
|
||||
|
||||
Every player gets a limited number of **Trade Slots** (default 8). Slots are shared across Active Listings, Active Bids, **and** Pending Claims — so uncollected claims take up space. Pick up your claims to free slots for new listings and bids!
|
||||
|
||||
> Server owners can grant players more slots via permissions up to a maximum of 45, or grant **unlimited slots** with the `customnpcs.auction.trades.unlimited` permission.
|
||||
|
||||
---
|
||||
|
||||
## Item Blacklist 🚫
|
||||
|
||||
Server owners have full control over what items can be listed on the Auction House.
|
||||
|
||||
**Block by:**
|
||||
- **Item** - Specific registry names like `minecraft:diamond_sword`
|
||||
- **Wildcard** - Patterns like `minecraft:diamond*` to block all diamond items
|
||||
- **Mod** - Block all items from a specific mod
|
||||
- **NBT Tag** - Block items containing certain NBT data
|
||||
|
||||
> **Soulbound** and **Profile-Slotbound** items are always blocked automatically, regardless of blacklist settings. Bound items cannot be traded!
|
||||
|
||||
**Blacklist Commands:**
|
||||
- `/kam auction blacklist add <item|mod|nbt> <value>` - Add to the blacklist
|
||||
- `/kam auction blacklist remove <item|mod|nbt> <value>` - Remove from the blacklist
|
||||
- `/kam auction blacklist list [item|mod|nbt]` - View blacklisted entries
|
||||
- `/kam auction blacklist reload` - Reload blacklist from config
|
||||
- `/kam auction blacklist check` - Check if your held item is blacklisted
|
||||
|
||||
---
|
||||
|
||||
## Trader Enhancements 🛒
|
||||
|
||||
### Stock System
|
||||
|
||||
Traders now support **limited stock** with automatic restocking!
|
||||
|
||||
**Stock Modes:**
|
||||
- **Global** - All players share the same stock pool
|
||||
- **Per-Player** - Each player has their own purchase limits
|
||||
|
||||
**Restock Timers:**
|
||||
- **MC Daily / Weekly** - Restock based on Minecraft time
|
||||
- **RL Daily / Weekly** - Restock based on real-life time
|
||||
- **Custom** - Set your own interval in MC ticks or real-time
|
||||
|
||||
Stock counts, out-of-stock status, and restock timers are all visible directly in the trade interface.
|
||||
|
||||
> Admins can manually reset stock or cooldowns via the Trader GUI.
|
||||
|
||||
### Currency Trades
|
||||
|
||||
Trades can now require **currency on top of items**. Combine material costs with coin costs for balanced economy progression!
|
||||
|
||||
---
|
||||
|
||||
## Auctioneer Role 🧑💼
|
||||
|
||||
New NPC role for the Auction House! Set the role on any NPC and players can access the full Auction House by interacting with them — no extra configuration needed. The role respects the global Auction Enabled setting.
|
||||
|
||||
---
|
||||
|
||||
## Admin Tools 🔨
|
||||
|
||||
### Admin Auction GUI
|
||||
|
||||
A dedicated management interface for server admins with three tabs:
|
||||
|
||||
- **Listings** - View and search all active auctions. Stop or cancel any listing
|
||||
- **Create** - Create server-managed listings with custom seller names, prices, and durations
|
||||
- **Claims** - Manage the global admin claims pool
|
||||
|
||||
### Auction Commands
|
||||
|
||||
**Player Commands:**
|
||||
- `/kam auction open` - Opens the Auction House GUI
|
||||
|
||||
**Admin Commands:**
|
||||
- `/kam auction list [page]` - View active auctions in chat
|
||||
- `/kam auction view <player>` - View a player's auction activity
|
||||
|
||||
> Server owners can enable **auction logging** to track all auction activity — creates, bids, buyouts, sales, expirations, cancellations, and claims. Each log type can be toggled individually.
|
||||
|
||||
---
|
||||
|
||||
## Scripting API 📜
|
||||
|
||||
Full event support for auction interactions. All events are **cancelable**, allowing scripts to intercept and control auction behavior:
|
||||
|
||||
- **CreateEvent** - Before an auction is created
|
||||
- **BidEvent** - Before a bid is placed
|
||||
- **BuyoutEvent** - Before an instant purchase
|
||||
- **CancelEvent** - Before a listing is cancelled
|
||||
- **ClaimEvent** - Before a claim is collected
|
||||
|
||||
---
|
||||
@@ -4,6 +4,7 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.entity.EntityAbilityBeam;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.Vec3;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -51,19 +52,20 @@ public class RenderEnergyBeam extends RenderEnergy {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get interpolated head offset
|
||||
// Get interpolated head offset (for head/trail rendering relative to origin)
|
||||
double headOffsetX = beam.getInterpolatedHeadOffsetX(partialTicks);
|
||||
double headOffsetY = beam.getInterpolatedHeadOffsetY(partialTicks);
|
||||
double headOffsetZ = beam.getInterpolatedHeadOffsetZ(partialTicks);
|
||||
|
||||
// The render system passes x,y,z as the position to render the entity at
|
||||
// Entity position = origin + headOffset (in world coords)
|
||||
// So origin render position = x,y,z - headOffset
|
||||
double renderOriginX = x - headOffsetX;
|
||||
double renderOriginY = y - headOffsetY;
|
||||
double renderOriginZ = z - headOffsetZ;
|
||||
// Compute render origin directly from the world origin and camera position.
|
||||
// We can't derive it from MC's x,y,z because syncPositionStateToCurrent() snaps
|
||||
// lastTickPos = posX on every sync packet, killing MC's position interpolation.
|
||||
// The headOffset interpolation then mismatches, causing the origin to wobble.
|
||||
double renderOriginX = beam.getOriginX() - RenderManager.renderPosX;
|
||||
double renderOriginY = beam.getOriginY() - RenderManager.renderPosY;
|
||||
double renderOriginZ = beam.getOriginZ() - RenderManager.renderPosZ;
|
||||
|
||||
// Get beam properties (interpolated for smooth size transitions)
|
||||
// Get beam properties (lerp-smoothed for smooth size transitions)
|
||||
float beamWidth = beam.getInterpolatedBeamWidth(partialTicks);
|
||||
float headSize = beam.getInterpolatedHeadSize(partialTicks);
|
||||
int innerColor = beam.getInnerColor();
|
||||
@@ -77,21 +79,22 @@ public class RenderEnergyBeam extends RenderEnergy {
|
||||
// Calculate head distance from origin (for overlap check)
|
||||
double headDistFromOrigin = Math.sqrt(headOffsetX * headOffsetX + headOffsetY * headOffsetY + headOffsetZ * headOffsetZ);
|
||||
|
||||
// Render tail orb at origin (0,0,0) - only in anchored mode
|
||||
// Don't render if head is too close to origin (would cause overlap/extra orb appearance)
|
||||
if (beam.shouldRenderTailOrb() && headDistFromOrigin > headSize * 0.5) {
|
||||
renderTailOrb(headSize * 0.8f, innerColor, outerColor, beam.isOuterColorEnabled(), beam.getOuterColorWidth(), outerAlpha, beam.getInnerAlpha() * proximityAlpha);
|
||||
}
|
||||
|
||||
float innerAlpha = beam.getInnerAlpha() * proximityAlpha;
|
||||
|
||||
// Render trail segments with smooth connections (with fading for non-anchored)
|
||||
// Render trail segments first (with fading for non-anchored)
|
||||
if (beam.hasFadingTrail()) {
|
||||
renderFadingTrail(beam, trail, beamWidth, innerColor, outerColor, beam.isOuterColorEnabled(), beam.getOuterColorWidth(), innerAlpha);
|
||||
} else {
|
||||
renderSmoothTrail(trail, beamWidth, innerColor, outerColor, beam.isOuterColorEnabled(), beam.getOuterColorWidth(), innerAlpha);
|
||||
}
|
||||
|
||||
// Render tail orb at origin (0,0,0) AFTER trail so it draws on top
|
||||
// Don't render if head is too close to origin (would cause overlap/extra orb appearance)
|
||||
// Use target headSize (not lerped) so the threshold isn't inflated by lerp lag
|
||||
if (beam.shouldRenderTailOrb() && headDistFromOrigin > beam.getHeadSize() * 0.5) {
|
||||
renderTailOrb(beam.getHeadSize() * 0.8f, innerColor, outerColor, beam.isOuterColorEnabled(), beam.getOuterColorWidth(), outerAlpha, innerAlpha);
|
||||
}
|
||||
|
||||
// Render head at interpolated offset position
|
||||
renderHead(beam, headOffsetX, headOffsetY, headOffsetZ, headSize, innerColor, outerColor, outerAlpha, innerAlpha, partialTicks);
|
||||
|
||||
|
||||
@@ -405,6 +405,8 @@ public class AbilityController implements IAbilityHandler {
|
||||
Ability original = customAbilities.get(originalName);
|
||||
if (original == null || original.isBuiltIn()) return null;
|
||||
|
||||
int originalCatId = customAbilityCatMap.getOrDefault(originalName, CategoryManager.UNCATEGORIZED_ID);
|
||||
|
||||
NBTTagCompound nbt = original.writeNBT(true);
|
||||
nbt.setString("id", UUID.randomUUID().toString());
|
||||
|
||||
@@ -416,6 +418,11 @@ public class AbilityController implements IAbilityHandler {
|
||||
clone.setName(name);
|
||||
|
||||
saveCustomAbility(clone);
|
||||
|
||||
if (originalCatId > CategoryManager.UNCATEGORIZED_ID) {
|
||||
customAbilityCatMap.put(clone.getName(), originalCatId);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -770,6 +777,8 @@ public class AbilityController implements IAbilityHandler {
|
||||
ChainedAbility original = chainedAbilities.get(originalName);
|
||||
if (original == null) return null;
|
||||
|
||||
int originalCatId = chainedAbilityCatMap.getOrDefault(originalName, CategoryManager.UNCATEGORIZED_ID);
|
||||
|
||||
NBTTagCompound nbt = original.writeNBT(true);
|
||||
nbt.setString("Id", UUID.randomUUID().toString());
|
||||
|
||||
@@ -781,6 +790,11 @@ public class AbilityController implements IAbilityHandler {
|
||||
clone.setName(name);
|
||||
|
||||
saveChainedAbility(clone);
|
||||
|
||||
if (originalCatId > CategoryManager.UNCATEGORIZED_ID) {
|
||||
chainedAbilityCatMap.put(clone.getName(), originalCatId);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,15 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
FREE_TRAIL
|
||||
}
|
||||
|
||||
// Beam shape properties
|
||||
// Beam shape properties (target values - set by sync packets/scripts)
|
||||
private float beamWidth = 0.3f;
|
||||
private float headSize = 0.5f;
|
||||
private float prevBeamWidth = 0.3f;
|
||||
private float prevHeadSize = 0.5f;
|
||||
|
||||
// Lerp-smoothed render values (approach target each tick)
|
||||
private float renderBeamWidth = 0.3f;
|
||||
private float renderHeadSize = 0.5f;
|
||||
private float prevRenderBeamWidth = 0.3f;
|
||||
private float prevRenderHeadSize = 0.5f;
|
||||
|
||||
// Trail - list of points showing beam path (RELATIVE to origin!)
|
||||
private List<Vec3> trailPoints = new ArrayList<>();
|
||||
@@ -236,18 +240,26 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void updateProjectile() {
|
||||
// Save previous render values for sub-tick interpolation
|
||||
prevRenderBeamWidth = renderBeamWidth;
|
||||
prevRenderHeadSize = renderHeadSize;
|
||||
|
||||
// Handle charging state (windup phase) - use isCharging() for synced value
|
||||
if (isCharging()) {
|
||||
// During charging, snap render values to target (size grows smoothly already)
|
||||
renderBeamWidth = beamWidth;
|
||||
renderHeadSize = headSize;
|
||||
updateCharging();
|
||||
return;
|
||||
}
|
||||
|
||||
// Store previous values for interpolation
|
||||
// Lerp render values toward target (smooth out sync packet jumps)
|
||||
renderBeamWidth += (beamWidth - renderBeamWidth) * 0.15f;
|
||||
renderHeadSize += (headSize - renderHeadSize) * 0.15f;
|
||||
|
||||
prevHeadOffsetX = headOffsetX;
|
||||
prevHeadOffsetY = headOffsetY;
|
||||
prevHeadOffsetZ = headOffsetZ;
|
||||
prevBeamWidth = beamWidth;
|
||||
prevHeadSize = headSize;
|
||||
|
||||
// Age trail points for fading effect
|
||||
if (isFadingMode()) {
|
||||
@@ -585,11 +597,10 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public float getInterpolatedBeamWidth(float partialTicks) {
|
||||
return prevBeamWidth + (beamWidth - prevBeamWidth) * partialTicks;
|
||||
return prevRenderBeamWidth + (renderBeamWidth - prevRenderBeamWidth) * partialTicks;
|
||||
}
|
||||
|
||||
public void setBeamWidth(float beamWidth) {
|
||||
this.prevBeamWidth = this.beamWidth;
|
||||
this.beamWidth = beamWidth;
|
||||
}
|
||||
|
||||
@@ -598,11 +609,10 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public float getInterpolatedHeadSize(float partialTicks) {
|
||||
return prevHeadSize + (headSize - prevHeadSize) * partialTicks;
|
||||
return prevRenderHeadSize + (renderHeadSize - prevRenderHeadSize) * partialTicks;
|
||||
}
|
||||
|
||||
public void setHeadSize(float headSize) {
|
||||
this.prevHeadSize = this.headSize;
|
||||
this.headSize = headSize;
|
||||
}
|
||||
|
||||
@@ -761,8 +771,6 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void applyProjectileClientSyncData(NBTTagCompound nbt) {
|
||||
prevBeamWidth = beamWidth;
|
||||
prevHeadSize = headSize;
|
||||
beamWidth = nbt.getFloat("BeamWidth");
|
||||
headSize = nbt.getFloat("HeadSize");
|
||||
setAttachedToOwner(nbt.getBoolean("AttachedToOwner"));
|
||||
@@ -774,8 +782,10 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
protected void readProjectileNBT(NBTTagCompound nbt) {
|
||||
this.beamWidth = sanitize(nbt.hasKey("BeamWidth") ? nbt.getFloat("BeamWidth") : 0.3f, 0.3f, MAX_ENTITY_SIZE);
|
||||
this.headSize = sanitize(nbt.hasKey("HeadSize") ? nbt.getFloat("HeadSize") : 0.5f, 0.5f, MAX_ENTITY_SIZE);
|
||||
this.prevBeamWidth = this.beamWidth;
|
||||
this.prevHeadSize = this.headSize;
|
||||
this.renderBeamWidth = this.beamWidth;
|
||||
this.renderHeadSize = this.headSize;
|
||||
this.prevRenderBeamWidth = this.beamWidth;
|
||||
this.prevRenderHeadSize = this.headSize;
|
||||
this.headOffsetX = nbt.hasKey("HeadOffsetX") ? nbt.getDouble("HeadOffsetX") : 0;
|
||||
this.headOffsetY = nbt.hasKey("HeadOffsetY") ? nbt.getDouble("HeadOffsetY") : 0;
|
||||
this.headOffsetZ = nbt.hasKey("HeadOffsetZ") ? nbt.getDouble("HeadOffsetZ") : 0;
|
||||
|
||||
@@ -30,11 +30,15 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
private boolean returning = false;
|
||||
private int ticksSinceMiss = 0;
|
||||
|
||||
// Disc shape properties
|
||||
// Disc shape properties (target values)
|
||||
private float discRadius = 1.0f; // Width of disc
|
||||
private float discThickness = 0.2f; // Height of disc
|
||||
private float prevDiscRadius = 1.0f;
|
||||
private float prevDiscThickness = 0.2f;
|
||||
|
||||
// Lerp-smoothed render values
|
||||
private float renderDiscRadius = 1.0f;
|
||||
private float renderDiscThickness = 0.2f;
|
||||
private float prevRenderDiscRadius = 1.0f;
|
||||
private float prevRenderDiscThickness = 0.2f;
|
||||
|
||||
// Boomerang owner-gone tracking
|
||||
private int returnOwnerNullTicks = 0;
|
||||
@@ -136,16 +140,23 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void updateProjectile() {
|
||||
// Store previous values for interpolation
|
||||
prevDiscRadius = discRadius;
|
||||
prevDiscThickness = discThickness;
|
||||
// Save previous render values for sub-tick interpolation
|
||||
prevRenderDiscRadius = renderDiscRadius;
|
||||
prevRenderDiscThickness = renderDiscThickness;
|
||||
|
||||
// Handle charging state (windup phase)
|
||||
if (isCharging()) {
|
||||
// During charging, snap render values to target (size grows smoothly already)
|
||||
renderDiscRadius = discRadius;
|
||||
renderDiscThickness = discThickness;
|
||||
updateCharging();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lerp render values toward target (smooth out sync packet jumps)
|
||||
renderDiscRadius += (discRadius - renderDiscRadius) * 0.15f;
|
||||
renderDiscThickness += (discThickness - renderDiscThickness) * 0.15f;
|
||||
|
||||
// In preview mode, run movement on client (no server)
|
||||
if (previewMode) {
|
||||
updatePreviewMovement();
|
||||
@@ -354,11 +365,10 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public float getInterpolatedDiscRadius(float partialTicks) {
|
||||
return prevDiscRadius + (discRadius - prevDiscRadius) * partialTicks;
|
||||
return prevRenderDiscRadius + (renderDiscRadius - prevRenderDiscRadius) * partialTicks;
|
||||
}
|
||||
|
||||
public void setDiscRadius(float radius) {
|
||||
this.prevDiscRadius = this.discRadius;
|
||||
this.discRadius = radius;
|
||||
}
|
||||
|
||||
@@ -367,11 +377,10 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public float getInterpolatedDiscThickness(float partialTicks) {
|
||||
return prevDiscThickness + (discThickness - prevDiscThickness) * partialTicks;
|
||||
return prevRenderDiscThickness + (renderDiscThickness - prevRenderDiscThickness) * partialTicks;
|
||||
}
|
||||
|
||||
public void setDiscThickness(float thickness) {
|
||||
this.prevDiscThickness = this.discThickness;
|
||||
this.discThickness = thickness;
|
||||
}
|
||||
|
||||
@@ -459,8 +468,6 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void applyProjectileClientSyncData(NBTTagCompound nbt) {
|
||||
prevDiscRadius = discRadius;
|
||||
prevDiscThickness = discThickness;
|
||||
discRadius = nbt.getFloat("DiscRadius");
|
||||
discThickness = nbt.getFloat("DiscThickness");
|
||||
vertical = nbt.getBoolean("Vertical");
|
||||
@@ -474,8 +481,10 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
this.boomerangDelay = nbt.hasKey("BoomerangDelay") ? nbt.getInteger("BoomerangDelay") : 40;
|
||||
this.discRadius = sanitize(nbt.hasKey("DiscRadius") ? nbt.getFloat("DiscRadius") : 1.0f, 1.0f, MAX_ENTITY_SIZE);
|
||||
this.discThickness = sanitize(nbt.hasKey("DiscThickness") ? nbt.getFloat("DiscThickness") : 0.2f, 0.2f, MAX_ENTITY_SIZE);
|
||||
this.prevDiscRadius = this.discRadius;
|
||||
this.prevDiscThickness = this.discThickness;
|
||||
this.renderDiscRadius = this.discRadius;
|
||||
this.renderDiscThickness = this.discThickness;
|
||||
this.prevRenderDiscRadius = this.discRadius;
|
||||
this.prevRenderDiscThickness = this.discThickness;
|
||||
this.vertical = nbt.hasKey("Vertical") ? nbt.getBoolean("Vertical") : false;
|
||||
this.returning = nbt.hasKey("Returning") && nbt.getBoolean("Returning");
|
||||
// Charging state (common fields handled by base)
|
||||
|
||||
@@ -31,9 +31,12 @@ import java.util.List;
|
||||
*/
|
||||
public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
|
||||
// Laser-specific properties
|
||||
// Laser-specific properties (target value)
|
||||
private float laserWidth = 0.2f;
|
||||
private float prevLaserWidth = 0.2f;
|
||||
|
||||
// Lerp-smoothed render values
|
||||
private float renderLaserWidth = 0.2f;
|
||||
private float prevRenderLaserWidth = 0.2f;
|
||||
private float expansionSpeed = 2.0f; // Blocks per tick
|
||||
private float maxLength = 32.0f; // Maximum beam reach in blocks
|
||||
|
||||
@@ -145,14 +148,19 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void updateProjectile() {
|
||||
// Store previous values for interpolation
|
||||
prevLaserWidth = laserWidth;
|
||||
// Save previous render value for sub-tick interpolation
|
||||
prevRenderLaserWidth = renderLaserWidth;
|
||||
|
||||
if (isCharging()) {
|
||||
// During charging, snap render value to target
|
||||
renderLaserWidth = laserWidth;
|
||||
updateCharging();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lerp render value toward target (smooth out sync packet jumps)
|
||||
renderLaserWidth += (laserWidth - renderLaserWidth) * 0.15f;
|
||||
|
||||
// Save previous positions for partial tick interpolation
|
||||
prevStartX = startX;
|
||||
prevStartY = startY;
|
||||
@@ -708,11 +716,10 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public float getInterpolatedLaserWidth(float partialTicks) {
|
||||
return prevLaserWidth + (laserWidth - prevLaserWidth) * partialTicks;
|
||||
return prevRenderLaserWidth + (renderLaserWidth - prevRenderLaserWidth) * partialTicks;
|
||||
}
|
||||
|
||||
public void setLaserWidth(float width) {
|
||||
this.prevLaserWidth = this.laserWidth;
|
||||
this.laserWidth = width;
|
||||
}
|
||||
|
||||
@@ -888,7 +895,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
|
||||
@Override
|
||||
protected void applyProjectileClientSyncData(NBTTagCompound nbt) {
|
||||
prevLaserWidth = laserWidth;
|
||||
laserWidth = nbt.getFloat("LaserWidth");
|
||||
expansionSpeed = nbt.getFloat("ExpansionSpeed");
|
||||
maxLength = nbt.getFloat("MaxLength");
|
||||
@@ -902,7 +908,8 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
@Override
|
||||
protected void readProjectileNBT(NBTTagCompound nbt) {
|
||||
this.laserWidth = sanitize(nbt.hasKey("LaserWidth") ? nbt.getFloat("LaserWidth") : 0.2f, 0.2f, MAX_ENTITY_SIZE);
|
||||
this.prevLaserWidth = this.laserWidth;
|
||||
this.renderLaserWidth = this.laserWidth;
|
||||
this.prevRenderLaserWidth = this.laserWidth;
|
||||
this.expansionSpeed = sanitize(nbt.hasKey("ExpansionSpeed") ? nbt.getFloat("ExpansionSpeed") : 2.0f, 0.1f, MAX_ENTITY_SIZE);
|
||||
this.maxLength = sanitize(nbt.hasKey("MaxLength") ? nbt.getFloat("MaxLength") : 32.0f, 1.0f, MAX_ENTITY_SIZE);
|
||||
this.dirX = nbt.getDouble("DirX");
|
||||
|
||||
@@ -101,12 +101,14 @@ import kamkeel.npcs.network.packets.request.category.CategoryListRequestPacket;
|
||||
import kamkeel.npcs.network.packets.request.category.CategoryMoveItemPacket;
|
||||
import kamkeel.npcs.network.packets.request.category.CategoryRemovePacket;
|
||||
import kamkeel.npcs.network.packets.request.category.CategorySavePacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CopyAbilityScriptsPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilityClonePacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationRemovePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationClonePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationSavePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationsGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.BuiltInAnimationGetPacket;
|
||||
@@ -498,6 +500,7 @@ public class PacketHandler {
|
||||
REQUEST_PACKET.registerPacket(new AnimationGetPacket());
|
||||
REQUEST_PACKET.registerPacket(new AnimationRemovePacket());
|
||||
REQUEST_PACKET.registerPacket(new AnimationSavePacket());
|
||||
REQUEST_PACKET.registerPacket(new AnimationClonePacket());
|
||||
REQUEST_PACKET.registerPacket(new BuiltInAnimationGetPacket());
|
||||
|
||||
// Moving Path
|
||||
@@ -541,6 +544,7 @@ public class PacketHandler {
|
||||
REQUEST_PACKET.registerPacket(new ChainedAbilityRemovePacket());
|
||||
REQUEST_PACKET.registerPacket(new CustomAbilityClonePacket());
|
||||
REQUEST_PACKET.registerPacket(new ChainedAbilityClonePacket());
|
||||
REQUEST_PACKET.registerPacket(new CopyAbilityScriptsPacket());
|
||||
|
||||
// Category Packets
|
||||
REQUEST_PACKET.registerPacket(new CategorySavePacket());
|
||||
|
||||
@@ -93,6 +93,7 @@ public enum EnumRequestPacket {
|
||||
AnimationGet,
|
||||
AnimationRemove,
|
||||
AnimationSave,
|
||||
AnimationClone,
|
||||
BuiltInAnimationGet,
|
||||
|
||||
RemoteNpcsGet,
|
||||
@@ -226,6 +227,7 @@ public enum EnumRequestPacket {
|
||||
|
||||
CustomAbilityClone,
|
||||
ChainedAbilityClone,
|
||||
CopyAbilityScripts,
|
||||
LinkedItemClone,
|
||||
EffectClone,
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package kamkeel.npcs.network.packets.request.ability;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.network.AbstractPacket;
|
||||
import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.PacketUtil;
|
||||
import kamkeel.npcs.network.enums.EnumItemPacketType;
|
||||
import kamkeel.npcs.network.enums.EnumRequestPacket;
|
||||
import kamkeel.npcs.util.ByteBufUtils;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.CustomNpcsPermissions;
|
||||
import noppes.npcs.controllers.data.AbilityScript;
|
||||
import noppes.npcs.controllers.data.ChainedAbilityScript;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Copies script data from a source ability/chain to a target UUID.
|
||||
* Used when cloning into an NPC as inline - the clone gets a new UUID
|
||||
* but needs the source's script handler copied server-side (scripts are never sent to client).
|
||||
*
|
||||
* Mode 0 = ability scripts, Mode 1 = chained ability scripts.
|
||||
*/
|
||||
public final class CopyAbilityScriptsPacket extends AbstractPacket {
|
||||
public static String packetName = "Request|CopyAbilityScripts";
|
||||
|
||||
public static final int MODE_ABILITY = 0;
|
||||
public static final int MODE_CHAINED = 1;
|
||||
|
||||
private int mode;
|
||||
private String sourceName;
|
||||
private String targetId;
|
||||
|
||||
public CopyAbilityScriptsPacket() {
|
||||
}
|
||||
|
||||
public CopyAbilityScriptsPacket(String sourceName, String targetId) {
|
||||
this(MODE_ABILITY, sourceName, targetId);
|
||||
}
|
||||
|
||||
public CopyAbilityScriptsPacket(int mode, String sourceName, String targetId) {
|
||||
this.mode = mode;
|
||||
this.sourceName = sourceName;
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enum getType() {
|
||||
return EnumRequestPacket.CopyAbilityScripts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketChannel getChannel() {
|
||||
return PacketHandler.REQUEST_PACKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomNpcsPermissions.Permission getPermission() {
|
||||
return CustomNpcsPermissions.GLOBAL_ABILITY;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void sendData(ByteBuf out) throws IOException {
|
||||
out.writeByte(mode);
|
||||
ByteBufUtils.writeString(out, sourceName);
|
||||
ByteBufUtils.writeString(out, targetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
|
||||
if (!(player instanceof EntityPlayerMP))
|
||||
return;
|
||||
|
||||
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
|
||||
return;
|
||||
|
||||
int mode = in.readByte();
|
||||
String sourceName = ByteBufUtils.readString(in);
|
||||
String targetId = ByteBufUtils.readString(in);
|
||||
|
||||
if (targetId == null || targetId.isEmpty()) return;
|
||||
|
||||
if (mode == MODE_CHAINED) {
|
||||
copyChainedAbilityScripts(sourceName, targetId);
|
||||
} else {
|
||||
copyAbilityScripts(sourceName, targetId);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyAbilityScripts(String sourceKey, String targetId) {
|
||||
// Try name-based lookup first
|
||||
Ability source = AbilityController.Instance.getCustomAbility(sourceKey);
|
||||
|
||||
AbilityScript sourceHandler;
|
||||
if (source != null) {
|
||||
sourceHandler = source.getScriptHandler();
|
||||
} else {
|
||||
// sourceKey might be a UUID - try direct handler lookup
|
||||
sourceHandler = AbilityController.Instance.abilityScriptHandlers.get(sourceKey);
|
||||
}
|
||||
if (sourceHandler == null) return;
|
||||
|
||||
NBTTagCompound scriptNbt = sourceHandler.writeToNBT(new NBTTagCompound());
|
||||
AbilityScript targetHandler = new AbilityScript(targetId);
|
||||
targetHandler.readFromNBT(scriptNbt);
|
||||
AbilityController.Instance.abilityScriptHandlers.put(targetId, targetHandler);
|
||||
}
|
||||
|
||||
private void copyChainedAbilityScripts(String sourceName, String targetId) {
|
||||
ChainedAbility source = AbilityController.Instance.getChainedAbility(sourceName);
|
||||
if (source == null) return;
|
||||
|
||||
ChainedAbilityScript sourceHandler = source.getScriptHandler();
|
||||
if (sourceHandler == null) return;
|
||||
|
||||
NBTTagCompound scriptNbt = sourceHandler.writeToNBT(new NBTTagCompound());
|
||||
ChainedAbilityScript targetHandler = new ChainedAbilityScript(targetId);
|
||||
targetHandler.readFromNBT(scriptNbt);
|
||||
AbilityController.Instance.chainedAbilityScriptHandlers.put(targetId, targetHandler);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package kamkeel.npcs.network.packets.request.animation;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import kamkeel.npcs.network.AbstractPacket;
|
||||
import kamkeel.npcs.network.PacketChannel;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.PacketUtil;
|
||||
import kamkeel.npcs.network.enums.EnumItemPacketType;
|
||||
import kamkeel.npcs.network.enums.EnumRequestPacket;
|
||||
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import noppes.npcs.CustomNpcsPermissions;
|
||||
import noppes.npcs.NoppesUtilServer;
|
||||
import noppes.npcs.controllers.AnimationController;
|
||||
import noppes.npcs.controllers.data.Animation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class AnimationClonePacket extends AbstractPacket {
|
||||
public static String packetName = "Request|AnimationClone";
|
||||
|
||||
private int id;
|
||||
|
||||
public AnimationClonePacket() {
|
||||
}
|
||||
|
||||
public AnimationClonePacket(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enum getType() {
|
||||
return EnumRequestPacket.AnimationClone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketChannel getChannel() {
|
||||
return PacketHandler.REQUEST_PACKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomNpcsPermissions.Permission getPermission() {
|
||||
return CustomNpcsPermissions.GLOBAL_ANIMATION;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void sendData(ByteBuf out) throws IOException {
|
||||
out.writeInt(this.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
|
||||
if (!(player instanceof EntityPlayerMP))
|
||||
return;
|
||||
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
|
||||
return;
|
||||
|
||||
int animationId = in.readInt();
|
||||
Animation clone = AnimationController.getInstance().cloneAnimation(animationId);
|
||||
if (clone != null) {
|
||||
NoppesUtilServer.sendAnimationDataAll((EntityPlayerMP) player);
|
||||
GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeToNBT());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
import kamkeel.npcs.network.packets.request.ability.CopyAbilityScriptsPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.AbilitiesGetAllPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcSavePacket;
|
||||
@@ -816,6 +817,14 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
* Adds as an inline slot.
|
||||
*/
|
||||
public void loadAbility(Ability loadedAbility) {
|
||||
loadAbility(loadedAbility, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from SubGuiAbilityLoad when a cloned ability is loaded with a known source.
|
||||
* Copies scripts server-side from source to the new inline ability.
|
||||
*/
|
||||
public void loadAbility(Ability loadedAbility, String sourceAbilityName) {
|
||||
if (loadedAbility != null) {
|
||||
loadedAbility.setId(UUID.randomUUID().toString());
|
||||
npcSlots.add(AbilityAction.inline(loadedAbility));
|
||||
@@ -825,6 +834,10 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
selectAbilityByIndex(selectedAbilityIndex);
|
||||
initGui();
|
||||
save();
|
||||
// Copy scripts from source ability server-side (scripts are never sent to client)
|
||||
if (sourceAbilityName != null && !sourceAbilityName.isEmpty()) {
|
||||
PacketClient.sendClient(new CopyAbilityScriptsPacket(sourceAbilityName, loadedAbility.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,12 +994,18 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
if (slot.isChain() && selectedEntryIndex == -1) {
|
||||
// Chain header (must be CHAIN_REFERENCE)
|
||||
if (mode == SubGuiAbilityEditMode.MODE_CLONE_MODIFY) {
|
||||
String sourceChainName = slot.getReferenceId();
|
||||
if (slot.convertToInline()) {
|
||||
ChainedAbility chain = slot.getInlineChain();
|
||||
if (chain != null) {
|
||||
pendingChain = chain;
|
||||
pendingChainSlotIdx = selectedSlotIndex;
|
||||
setSubGui(new SubGuiChainedAbilityConfig(chain, this, true, npcSlots));
|
||||
// Copy chain scripts server-side (client never has script handlers)
|
||||
if (sourceChainName != null && !sourceChainName.isEmpty()) {
|
||||
PacketClient.sendClient(new CopyAbilityScriptsPacket(
|
||||
CopyAbilityScriptsPacket.MODE_CHAINED, sourceChainName, chain.getId()));
|
||||
}
|
||||
}
|
||||
updateNpcAbilitiesList();
|
||||
save();
|
||||
@@ -1015,6 +1034,8 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
ChainedAbility chain = slot.getInlineChain();
|
||||
if (chain != null && selectedEntryIndex < chain.getEntries().size()) {
|
||||
ChainedAbilityEntry entry = chain.getEntries().get(selectedEntryIndex);
|
||||
// Capture source ability reference before convertToInline clears it
|
||||
String sourceAbilityRef = entry.getAbilityReference();
|
||||
if (entry.convertToInline()) {
|
||||
Ability a = entry.getInlineAbility();
|
||||
if (a != null && !a.isBuiltIn()) {
|
||||
@@ -1023,6 +1044,10 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
editChainEntryIdx = selectedEntryIndex;
|
||||
a.setNpcInlineEdit(true);
|
||||
setSubGui(a.createConfigGui(this));
|
||||
// Copy ability scripts server-side (client never has script handlers)
|
||||
if (sourceAbilityRef != null && !sourceAbilityRef.isEmpty()) {
|
||||
PacketClient.sendClient(new CopyAbilityScriptsPacket(sourceAbilityRef, a.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1043,12 +1068,18 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
// Standalone ability reference → convert to inline
|
||||
Ability preCheck = slot.getAbility();
|
||||
if (preCheck != null && preCheck.isBuiltIn()) return;
|
||||
// Capture source reference before convertToInline clears it
|
||||
String sourceAbilityRef = slot.getReferenceId();
|
||||
if (slot.convertToInline()) {
|
||||
Ability ability = slot.getAbility();
|
||||
if (ability != null && !ability.isBuiltIn()) {
|
||||
ability.setId(UUID.randomUUID().toString());
|
||||
// UUID already generated by convertToInline() - don't reassign or script handler is orphaned
|
||||
ability.setNpcInlineEdit(true);
|
||||
setSubGui(ability.createConfigGui(this));
|
||||
// Copy ability scripts server-side (client never has script handlers)
|
||||
if (sourceAbilityRef != null && !sourceAbilityRef.isEmpty()) {
|
||||
PacketClient.sendClient(new CopyAbilityScriptsPacket(sourceAbilityRef, ability.getId()));
|
||||
}
|
||||
}
|
||||
updateNpcAbilitiesList();
|
||||
save();
|
||||
|
||||
@@ -216,7 +216,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
|
||||
if (parentConfig != null) {
|
||||
parentConfig.loadAbility(loaded);
|
||||
} else if (parentAbilities != null) {
|
||||
parentAbilities.loadAbility(loaded);
|
||||
parentAbilities.loadAbility(loaded, selectedName);
|
||||
}
|
||||
}
|
||||
close();
|
||||
|
||||
@@ -3,6 +3,7 @@ package noppes.npcs.client.gui.global;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationRemovePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationClonePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationSavePacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.AnimationsGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.animation.BuiltInAnimationGetPacket;
|
||||
@@ -169,11 +170,7 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
|
||||
protected void onCloneItem() {
|
||||
if (showingBuiltIn || currentIsBuiltIn) return;
|
||||
if (animation != null && animation.id >= 0) {
|
||||
Animation clone = new Animation();
|
||||
clone.readFromNBT(animation.writeToNBT());
|
||||
clone.id = -1;
|
||||
while (itemData.containsKey(clone.name)) clone.name += "_";
|
||||
PacketClient.sendClient(new AnimationSavePacket(clone.writeToNBT()));
|
||||
PacketClient.sendClient(new AnimationClonePacket(animation.id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -544,65 +544,80 @@ public class RenderNPCInterface extends RenderLiving {
|
||||
|
||||
@Override
|
||||
public ResourceLocation getEntityTexture(Entity entity) {
|
||||
|
||||
EntityNPCInterface npc = (EntityNPCInterface) entity;
|
||||
if (npc.textureLocation == null) {
|
||||
if (npc.display.skinType == 0) {
|
||||
|
||||
// Early exit if texture already resolved
|
||||
if (npc.textureLocation != null) {
|
||||
return npc.textureLocation;
|
||||
}
|
||||
|
||||
// SkinType 0: custom texture or default
|
||||
if (npc.display.skinType == 0) {
|
||||
if (npc.display.texture.isEmpty()) {
|
||||
return npc.textureLocation = fallBackSkin(npc);
|
||||
}
|
||||
|
||||
try {
|
||||
if (npc instanceof EntityCustomNpc && ((EntityCustomNpc) npc).modelData.entityClass == null) {
|
||||
if (!(npc.display.texture).isEmpty()) {
|
||||
try {
|
||||
npc.textureLocation = adjustLocalTexture(npc, new ResourceLocation(npc.display.texture));
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
npc.textureLocation = adjustLocalTexture(npc, new ResourceLocation(npc.display.texture));
|
||||
} else {
|
||||
npc.textureLocation = new ResourceLocation(npc.display.texture);
|
||||
ResourceLocation resLoc = new ResourceLocation(npc.display.texture);
|
||||
Minecraft.getMinecraft().getResourceManager().getResource(resLoc);
|
||||
npc.textureLocation = resLoc;
|
||||
}
|
||||
} else if (npc.display.skinType == 1 && npc.display.playerProfile != null) {
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
Map map = minecraft.func_152342_ad().func_152788_a(npc.display.playerProfile);
|
||||
if (map.containsKey(Type.SKIN)) {
|
||||
npc.textureLocation = minecraft.func_152342_ad().func_152792_a((MinecraftProfileTexture) map.get(Type.SKIN), Type.SKIN);
|
||||
}
|
||||
LastTextureTick = 0;
|
||||
} else if (npc.display.skinType == 2 || npc.display.skinType == 3) {
|
||||
ResourceLocation location = new ResourceLocation("skins/" + (npc.display.skinType + npc.display.url).hashCode());
|
||||
// If URL Empty Steve
|
||||
if (npc.display.url.isEmpty()) {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
// If URL Cached then grab it
|
||||
else if (ClientCacheHandler.isCachedNPC(location)) {
|
||||
try {
|
||||
ResourceLocation loc = ClientCacheHandler.getNPCTexture(npc.display.url, npc.display.skinType == 3, location).getLocation();
|
||||
if (loc != null) {
|
||||
npc.textureLocation = loc;
|
||||
} else {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
// For New URL Requests do not spam it
|
||||
else if (LastTextureTick < 5) { //fixes request flood somewhat
|
||||
return fallBackSkin(npc);
|
||||
} else {
|
||||
try {
|
||||
ResourceLocation loc = ClientCacheHandler.getNPCTexture(npc.display.url, npc.display.skinType == 3, location).getLocation();
|
||||
if (loc != null) {
|
||||
npc.textureLocation = loc;
|
||||
} else {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
LastTextureTick = 0;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} catch (IOException ignored) {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
}
|
||||
return npc.textureLocation;
|
||||
|
||||
// SkinType 1: player profile skin
|
||||
else if (npc.display.skinType == 1 && npc.display.playerProfile != null) {
|
||||
final Minecraft minecraft = Minecraft.getMinecraft();
|
||||
Map map = minecraft.func_152342_ad().func_152788_a(npc.display.playerProfile);
|
||||
if (map.containsKey(Type.SKIN)) {
|
||||
npc.textureLocation = minecraft.func_152342_ad()
|
||||
.func_152792_a((MinecraftProfileTexture) map.get(Type.SKIN), Type.SKIN);
|
||||
}
|
||||
LastTextureTick = 0;
|
||||
}
|
||||
|
||||
// SkinType 2 / 3: load from URL
|
||||
else if (npc.display.skinType == 2 || npc.display.skinType == 3) {
|
||||
if (npc.display.url.isEmpty()) { // If URL is empty → fallback
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
|
||||
ResourceLocation location = new ResourceLocation("skins/" + (npc.display.skinType + npc.display.url).hashCode());
|
||||
|
||||
try {
|
||||
if (ClientCacheHandler.isCachedNPC(location)) { // If URL is cached
|
||||
ResourceLocation loc = ClientCacheHandler
|
||||
.getNPCTexture(npc.display.url, npc.display.skinType == 3, location)
|
||||
.getLocation();
|
||||
if (loc == null) return fallBackSkin(npc);
|
||||
npc.textureLocation = loc;
|
||||
} else if (LastTextureTick >= 5) { // Prevent request flooding
|
||||
ResourceLocation loc = ClientCacheHandler
|
||||
.getNPCTexture(npc.display.url, npc.display.skinType == 3, location)
|
||||
.getLocation();
|
||||
if (loc == null) return fallBackSkin(npc);
|
||||
npc.textureLocation = loc;
|
||||
LastTextureTick = 0;
|
||||
} else {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: if no valid skin type matched
|
||||
else {
|
||||
return fallBackSkin(npc);
|
||||
}
|
||||
|
||||
// Final safety net
|
||||
return npc.textureLocation == null ? fallBackSkin(npc) : npc.textureLocation;
|
||||
}
|
||||
|
||||
private ResourceLocation adjustLocalTexture(EntityNPCInterface npc, ResourceLocation location) throws IOException {
|
||||
|
||||
@@ -311,6 +311,28 @@ public class AnimationController implements IAnimationHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Animation cloneAnimation(int originalId) {
|
||||
Animation original = animations.get(originalId);
|
||||
if (original == null || original instanceof BuiltInAnimation) return null;
|
||||
|
||||
int originalCatId = categoryManager.getItemCategory(originalId);
|
||||
|
||||
Animation clone = new Animation();
|
||||
clone.readFromNBT(original.writeToNBT());
|
||||
clone.id = getUnusedId();
|
||||
|
||||
String name = clone.name;
|
||||
while (hasName(name)) name += "_";
|
||||
clone.name = name;
|
||||
|
||||
if (originalCatId > CategoryManager.UNCATEGORIZED_ID) {
|
||||
categoryManager.registerItem(clone.id, originalCatId);
|
||||
}
|
||||
|
||||
saveAnimation(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void delete(String name) {
|
||||
Animation delete = getAnimationFromName(name);
|
||||
if (delete != null) {
|
||||
|
||||
@@ -712,6 +712,8 @@ public class CustomEffectController implements ICustomEffectHandler {
|
||||
CustomEffect original = getCustomEffects().get(originalId);
|
||||
if (original == null) return null;
|
||||
|
||||
int originalCatId = categoryManager.getItemCategory(originalId);
|
||||
|
||||
NBTTagCompound nbt = original.writeToNBT(true);
|
||||
int newId = getUnusedId();
|
||||
nbt.setInteger("ID", newId);
|
||||
@@ -723,6 +725,10 @@ public class CustomEffectController implements ICustomEffectHandler {
|
||||
while (has(name)) name += "_";
|
||||
clone.name = name;
|
||||
|
||||
if (originalCatId > CategoryManager.UNCATEGORIZED_ID) {
|
||||
categoryManager.registerItem(newId, originalCatId);
|
||||
}
|
||||
|
||||
saveEffect(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -245,6 +245,8 @@ public class LinkedItemController implements ILinkedItemHandler {
|
||||
LinkedItem original = linkedItems.get(originalId);
|
||||
if (original == null) return null;
|
||||
|
||||
int originalCatId = categoryManager.getItemCategory(originalId);
|
||||
|
||||
NBTTagCompound nbt = original.writeToNBT(true);
|
||||
int newId = getUnusedId();
|
||||
nbt.setInteger("Id", newId);
|
||||
@@ -256,6 +258,10 @@ public class LinkedItemController implements ILinkedItemHandler {
|
||||
while (hasName(name)) name += "_";
|
||||
clone.name = name;
|
||||
|
||||
if (originalCatId > CategoryManager.UNCATEGORIZED_ID) {
|
||||
categoryManager.registerItem(newId, originalCatId);
|
||||
}
|
||||
|
||||
saveLinkedItem(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package noppes.npcs.controllers.data;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import noppes.npcs.api.handler.data.IQuest;
|
||||
import noppes.npcs.api.handler.data.IQuestCategory;
|
||||
|
||||
@@ -21,8 +22,8 @@ public class QuestCategory implements IQuestCategory {
|
||||
public void readNBT(NBTTagCompound nbttagcompound) {
|
||||
id = nbttagcompound.getInteger("Slot");
|
||||
title = nbttagcompound.getString("Title");
|
||||
NBTTagList questList = nbttagcompound.getTagList("Quests", 10);
|
||||
if (questList != null) {
|
||||
NBTTagList questList = getFirstValidList(nbttagcompound, Constants.NBT.TAG_COMPOUND, "Quests", "Dialogs");
|
||||
if (questList.tagCount() > 0) {
|
||||
for (int ii = 0; ii < questList.tagCount(); ii++) {
|
||||
NBTTagCompound nbttagcompound2 = questList.getCompoundTagAt(ii);
|
||||
Quest quest = new Quest();
|
||||
@@ -33,6 +34,18 @@ public class QuestCategory implements IQuestCategory {
|
||||
}
|
||||
}
|
||||
|
||||
private NBTTagList getFirstValidList(NBTTagCompound nbt, int type, String... keys) {
|
||||
for (String key : keys) {
|
||||
if (nbt.hasKey(key, Constants.NBT.TAG_LIST)) {
|
||||
NBTTagList list = nbt.getTagList(key, type);
|
||||
if (list.tagCount() > 0) {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new NBTTagList();
|
||||
}
|
||||
|
||||
public NBTTagCompound writeNBT(NBTTagCompound nbttagcompound) {
|
||||
nbttagcompound.setInteger("Slot", id);
|
||||
nbttagcompound.setString("Title", title);
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
# Abilities
|
||||
|
||||
---
|
||||
|
||||
The **Ability System** adds full combat abilities to CustomNPC+. Give your NPCs powerful attacks with visual telegraphs, launch energy projectiles, set traps, chain abilities into combos, and let players unlock abilities of their own.
|
||||
|
||||
---
|
||||
|
||||
## How Abilities Work
|
||||
|
||||
Every ability follows a **phase-based flow**:
|
||||
|
||||
- **Wind-up** - The ability charges up. Telegraphs appear on the ground, animations play, and observant players can react
|
||||
- **Active** - The ability fires. Damage is dealt, projectiles launch, effects apply
|
||||
- **Dazed** - Optional recovery state if the ability gets interrupted during wind-up
|
||||
|
||||
**Settings you can configure:**
|
||||
- **Cooldown** - Time before the ability can be used again. Supports both a universal cooldown and per-ability cooldowns
|
||||
- **Targeting** - Aggro Target, Self, AOE on Self, or AOE on Target
|
||||
- **Range** - Minimum and maximum range for activation
|
||||
- **Lock Movement** - Lock the caster's movement, rotation, or both during any phase
|
||||
- **Interruptible** - Allow damage to interrupt the wind-up and force a dazed state
|
||||
- **Weight** - Priority weighting for NPC ability selection
|
||||
- **Invulnerability** - Make the caster invulnerable during Wind-up, Active, or both
|
||||
- **Ignore I-Frames** - Bypass the damage immunity timer for rapid consecutive hits
|
||||
- **Free on Cast** - The ability enters cooldown the moment it fires, freeing the caster while projectiles or zones persist on their own
|
||||
- **Magic Damage** - Deal magic damage as a separate damage type with configurable values and split ratios
|
||||
|
||||
> Abilities can be configured directly on individual NPCs or saved as **global presets** that update everywhere when edited.
|
||||
|
||||
---
|
||||
|
||||
## Melee Abilities
|
||||
|
||||
Close-range attacks for punishing positioning.
|
||||
|
||||
- **Slam** - Leap toward the target in an arc, dealing AOE damage on impact. Configure the radius, knockback, and leap height. Air slams deal reduced damage based on the required leap height
|
||||
- **Heavy Hit** - A single powerful melee strike with bonus knockback and a configurable hit delay
|
||||
- **Cutter** - A slashing arc attack in a cone. Configure the angle and reach
|
||||
|
||||
> All melee abilities support configurable damage, knockback, and potion effects on hit.
|
||||
|
||||
---
|
||||
|
||||
## Energy Projectiles
|
||||
|
||||
A full projectile system with deep visual customization. Energy projectiles use a **Variant System** — each base type can be configured with variants like dual-fire or barrage patterns.
|
||||
|
||||
### Types
|
||||
|
||||
- **Orb** - Homing energy spheres that track their target. Supports dual-fire and barrage variants
|
||||
- **Disc** - Flat spinning projectiles with optional boomerang behavior. Supports vertical and horizontal orientation
|
||||
- **Beam** - Sustained energy beams connecting the caster to their target. Supports Free Aim mode for manual aiming
|
||||
- **Laser Shot** - Instant-fire hitscan projectile with configurable width and speed. No homing
|
||||
|
||||
### Projectile Settings
|
||||
|
||||
Every energy projectile shares these configurable properties:
|
||||
|
||||
- **Anchor Point** - Where the projectile spawns: Right Hand, Left Hand, Front, Center, Chest, Eye, or Above Head. Supports custom offset anchors
|
||||
- **Display** - Core color, edge color, glow effect, transparency, and size
|
||||
- **Homing** - Toggle homing, adjust turn rate and max tracking range
|
||||
- **Combat** - Damage, knockback, vertical knockback, hit cooldown, and optional AOE splash
|
||||
- **Hit Type** - How the projectile interacts: Single (hits one), Pierce (passes through), or Multi (hits multiple). Pierce and Multi support a configurable max hit count
|
||||
- **Lifespan** - Speed, max age, and max travel distance (default 150 blocks)
|
||||
- **Size Interpolation** - Projectile size can smoothly change during flight
|
||||
- **Proximity Alpha** - Projectiles can fade based on distance from the viewer
|
||||
- **Explosions** - Detonate on impact with configurable radius. Block damage can be toggled
|
||||
|
||||
---
|
||||
|
||||
## Energy Barriers
|
||||
|
||||
Defensive energy constructs that block incoming attacks.
|
||||
|
||||
- **Dome** - A spherical barrier centered on the caster. Blocks incoming projectiles with configurable damage multipliers, HP, and duration
|
||||
- **Wall** - A flat energy panel with configurable dimensions. Has an optional launch mode that fires the wall forward, dealing damage and knockback
|
||||
- **Shield** - A held energy panel that follows the caster's facing direction
|
||||
|
||||
**Barrier Properties:**
|
||||
- HP and durability — barriers break when depleted
|
||||
- Configurable melee vulnerability
|
||||
- Damage absorption — attacks on the caster can be redirected to the barrier
|
||||
- Reflection — barriers can reflect projectiles back at the attacker
|
||||
- Ally/target filtering for which projectiles are blocked
|
||||
- Custom position offsets
|
||||
|
||||
---
|
||||
|
||||
## Defensive Abilities
|
||||
|
||||
Reactive stances that protect the caster when hit.
|
||||
|
||||
- **Guard** - Reduces incoming damage by a configurable percentage. Lasts until duration expires or max hit count is reached
|
||||
- **Counter** - Absorbs attacks and counter-attacks the attacker. Counter damage can be flat or a percentage of absorbed damage. Plays a counter animation on each trigger
|
||||
- **Dodge** - Fully evades incoming attacks, negating all damage. Plays randomized dodge animations from up to three configurable animation slots
|
||||
|
||||
> All defensive abilities support configurable duration and max hit counts.
|
||||
|
||||
---
|
||||
|
||||
## Movement Abilities
|
||||
|
||||
Reposition the caster during combat.
|
||||
|
||||
- **Charge** - Rush forward in a line, damaging everything in the path. Configure speed, hit width, and knockback
|
||||
- **Dash** - Quick directional movement without dealing damage. Supports directional key input to control the dash direction
|
||||
- **Teleport** - Instantly relocate to the target's position
|
||||
|
||||
> Charge locks direction during wind-up, so the telegraph accurately shows the attack path.
|
||||
|
||||
---
|
||||
|
||||
## AOE Abilities
|
||||
|
||||
Area control abilities that dominate space.
|
||||
|
||||
- **Shockwave** - AOE burst from the caster, dealing damage and knockback in a configurable radius
|
||||
- **Sweeper** - Wide rotational sweep around the caster with configurable radius and rotation speed. Can run alongside other abilities in chains
|
||||
- **Vortex** - Creates a pulling force that drags nearby entities toward the center while dealing damage
|
||||
|
||||
---
|
||||
|
||||
## Hazards & Traps
|
||||
|
||||
Ground-based abilities for area denial and ambushes.
|
||||
|
||||
### Hazards
|
||||
|
||||
Persistent damaging zones that continuously hurt entities inside them. Configure the damage interval, number of zones (up to 20), shape (circle or square), zone height, and duration. Can optionally affect the caster.
|
||||
|
||||
**Visual Presets:** Default, Toxic, Inferno, Arcane, Electric, Frost — each with themed colors, particles, and effects.
|
||||
|
||||
### Traps
|
||||
|
||||
Proximity-triggered abilities that sit dormant until an entity enters the trigger radius. Configure arm time, number of triggers, trigger cooldown, damage radius, and knockback. Traps can be hidden (invisible) until triggered.
|
||||
|
||||
**Trap Presets:**
|
||||
- **Hidden** - Minimal visibility, pure stealth trap
|
||||
- **Venom** - Applies Poison with toxic green visuals
|
||||
- **Explosive** - Applies Fire with fiery orange particles
|
||||
- **Cursed** - Applies Weakness with purple arcane effects
|
||||
- **Shock** - Applies Mining Fatigue with blue electrical sparks
|
||||
- **Snare** - Applies Slowness with icy blue visuals
|
||||
|
||||
### Zone Visuals
|
||||
|
||||
Both hazards and traps have a rich visual system with configurable ground fill, concentric rings, rotating borders, accents (static, swaying, or flickering), lightning arcs, and particle effects with multiple motion types.
|
||||
|
||||
---
|
||||
|
||||
## Ability Effect
|
||||
|
||||
A support ability for applying buffs, debuffs, healing, and custom effects.
|
||||
|
||||
**Targeting:**
|
||||
- **Self** - Affect only the caster
|
||||
- **AOE Self** - Area-of-effect centered on the caster
|
||||
|
||||
**Target Filter (AOE):**
|
||||
- **Allies** - Only friendly entities (determined by Party for players, Faction for NPCs)
|
||||
- **Enemies** - Only hostile entities
|
||||
- **All** - Everyone in range
|
||||
|
||||
**What it can apply:**
|
||||
- Flat and percentage-based healing (instant or over time)
|
||||
- Vanilla potion effects
|
||||
- Custom effects from the Custom Effect system
|
||||
- Mod-registered effect actions
|
||||
|
||||
**Built-in Variants:** Self Heal, Healing Aura, Poison Debuff.
|
||||
|
||||
---
|
||||
|
||||
## Custom Ability
|
||||
|
||||
A blank-slate ability type for scripters to build fully custom abilities through script hooks.
|
||||
|
||||
**Script Hooks:**
|
||||
- **Start** - When the ability begins
|
||||
- **Execute** - When entering the active phase
|
||||
- **Tick** - Every game tick with phase and tick count
|
||||
- **Complete** - When the ability finishes
|
||||
- **Toggle / Toggle Tick** - For toggle-mode abilities with multiple states
|
||||
|
||||
Full telegraph control with all shape types, configurable dimensions, colors, and duration. Supports both instant execution and multi-state toggle modes.
|
||||
|
||||
---
|
||||
|
||||
## Burst System
|
||||
|
||||
Abilities can fire **multiple times in rapid succession** as a single use.
|
||||
|
||||
- **Burst Amount** - Number of additional repetitions after the initial cast
|
||||
- **Burst Delay** - Ticks between each burst
|
||||
- **Replay Animations** - Optionally replay the wind-up animation on each burst
|
||||
- **Overlap Mode** - Previous entities persist while the next burst fires, allowing stacking effects
|
||||
|
||||
> Available on most ability types including energy projectiles, melee abilities, and movement abilities.
|
||||
|
||||
---
|
||||
|
||||
## Chained Abilities
|
||||
|
||||
Chain multiple abilities into **sequential combos** that execute one after another.
|
||||
|
||||
- **Ordered Entries** - Each entry references an ability with a configurable delay before it fires
|
||||
- **Wind-up All** - Optionally wind up all abilities simultaneously before the chain begins
|
||||
- **Chain Cooldown** - A single cooldown after the entire chain completes
|
||||
- **Chain Conditions** - Conditions checked once for the whole chain, not per-ability
|
||||
|
||||
### Concurrency
|
||||
|
||||
Specific ability types can run **concurrently** within a chain — executing alongside the primary ability instead of waiting. Concurrent-capable types include Effect, Hazard, Trap, and Sweeper.
|
||||
|
||||
> Chained abilities work with the ability hotbar and support live previewing in the editor.
|
||||
|
||||
---
|
||||
|
||||
## Telegraph System
|
||||
|
||||
**Telegraphs** are visual ground indicators that warn players before an ability lands. They appear during wind-up and shift color before the ability fires.
|
||||
|
||||
**Shapes:**
|
||||
- **Circle** - Filled circle with configurable radius
|
||||
- **Ring** - Donut shape with inner and outer radius
|
||||
- **Line** - Rectangle along the attack direction
|
||||
- **Cone** - Fan/wedge shape with configurable angle
|
||||
- **Point** - Single point indicator
|
||||
- **Square** - Square area indicator
|
||||
|
||||
**Features:**
|
||||
- ARGB color customization for both telegraph and warning phase
|
||||
- Color shifts in the final ticks before firing
|
||||
- Can follow a moving entity or stay at a fixed position
|
||||
- Proper ground and air height handling
|
||||
- Saved as reusable presets shared across abilities
|
||||
|
||||
---
|
||||
|
||||
## Animations & Sound
|
||||
|
||||
Abilities integrate directly with the animation and sound systems.
|
||||
|
||||
- **Wind-up Animation** - Plays during charging
|
||||
- **Active Animation** - Plays when the ability fires
|
||||
- **Dazed Animation** - Plays during recovery
|
||||
- **Animation Sync** - Optionally match wind-up duration to animation length
|
||||
- **Sound Events** - Custom sounds for wind-up and active phases
|
||||
- **Phase Colors** - Visual color feedback on the caster during wind-up and active
|
||||
- **Multiple Animations** - Multiple animations can be registered under one common name
|
||||
|
||||
> Many ability types include built-in animation names that work automatically when matching animations exist.
|
||||
|
||||
---
|
||||
|
||||
## Effects & Conditions
|
||||
|
||||
### Potion Effects
|
||||
|
||||
Abilities can apply potion effects to any entity they hit. Each effect has a configurable type, duration, and amplifier. Supports preset effect types and manual potion IDs for modded effects with amplification up to 255.
|
||||
|
||||
**Preset Effects:** Fire, Slowness, Weakness, Poison, Wither, Blindness, Nausea, Hunger, Mining Fatigue
|
||||
|
||||
### Conditions
|
||||
|
||||
Conditions control when an ability can be used. Each condition can check the **Caster**, the **Target**, or **Both**.
|
||||
|
||||
- **HP Threshold** - Health above, below, or equal to a value (flat or percentage)
|
||||
- **Hit Count** - Caster hit a certain number of times within a time window
|
||||
- **Has Item** - Entity holding, wearing, or carrying a specific item
|
||||
- **Quest Completed** - Player has completed a specific quest
|
||||
- **Has Effect** - Entity has a specific custom effect applied
|
||||
|
||||
---
|
||||
|
||||
## Player Abilities
|
||||
|
||||
Players can unlock and use abilities with a full hotbar system.
|
||||
|
||||
### Ability Hotbar
|
||||
|
||||
The **Ability Hotbar** is a 12-slot bar for equipping abilities. Hold the **HUD Key** and use the **scroll wheel** to cycle through equipped abilities. The hotbar shows ability icons with cooldown progress and supports horizontal or vertical layouts with 3, 5, or 7 visible slots.
|
||||
|
||||
Configure your hotbar through drag-and-drop in the **CNPC+ Inventory Ability Tab**, and customize its appearance in the **HUD Editor** under CNPC+ Inventory Settings.
|
||||
|
||||
### Player Features
|
||||
- **Unlock System** - Players receive abilities via scripting or commands
|
||||
- **Per-Ability Cooldowns** - Each ability can have its own cooldown on top of the universal cooldown
|
||||
- **Cancel on Double-Tap** - Double-tap the ability key during wind-up to cancel and enter cooldown immediately
|
||||
- **Ability Visibility** - Hide or show specific abilities in the HUD
|
||||
- **State Management** - Abilities are properly cancelled on logout, dimension change, death, and respawn. Movement input is suppressed during ability locks
|
||||
- **Flight Safety** - Abilities do not affect players who are flying
|
||||
|
||||
---
|
||||
|
||||
## Scripting API
|
||||
|
||||
Full scripting support for ability lifecycle events:
|
||||
|
||||
- **StartEvent** *(Cancelable)* - When wind-up begins
|
||||
- **ExecuteEvent** *(Cancelable)* - When the ability activates
|
||||
- **InterruptEvent** - When wind-up is interrupted
|
||||
- **CompleteEvent** - When the ability finishes
|
||||
- **HitEvent** *(Cancelable)* - On each entity hit. Damage and knockback can be modified
|
||||
- **TickEvent** - Every tick with current phase and tick count
|
||||
- **PlayerAbilityEvent** - For player ability actions
|
||||
|
||||
### Energy Projectile Scripting
|
||||
|
||||
Full scripting for all energy projectile types:
|
||||
- **Fired** - When the projectile launches
|
||||
- **Update** - Every tick during flight
|
||||
- **EntityImpact** - On hitting an entity
|
||||
- **BlockImpact** - On hitting a block
|
||||
- **Expired** - When the projectile's lifespan ends
|
||||
|
||||
### Ability Extender System
|
||||
|
||||
Register custom lifecycle hooks on abilities:
|
||||
- **onAbilityStart** - When the ability begins
|
||||
- **onAbilityTick** - Every tick during execution
|
||||
- **onAbilityComplete** - When the ability finishes
|
||||
- **onAbilityDamage** - When the ability deals damage
|
||||
|
||||
Multiple extenders can be registered per ability. Scripts are instanced — every use gets its own fresh set of variables.
|
||||
|
||||
---
|
||||
|
||||
## Ability Preview
|
||||
|
||||
The ability editor includes a **live preview system**. Test telegraph shapes, animations, projectile visuals, burst patterns, and chained ability sequences directly in the GUI on a dummy NPC — no world effects applied.
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
- `/kam ability list` - List all custom abilities
|
||||
- `/kam ability types` - List all registered ability types
|
||||
- `/kam ability prebuilts` - List all built-in abilities
|
||||
- `/kam ability info <name>` - View ability details
|
||||
- `/kam ability delete <name>` - Delete a custom ability
|
||||
- `/kam ability reload` - Reload abilities from disk
|
||||
- `/kam ability give <player> <ability>` - Give an ability to a player
|
||||
- `/kam ability remove <player> <ability>` - Remove an ability from a player
|
||||
- `/kam ability giveChain <player> <chain>` - Give a chained ability to a player
|
||||
- `/kam ability removeChain <player> <chain>` - Remove a chained ability from a player
|
||||
- `/kam ability player` - Manage player abilities
|
||||
|
||||
---
|
||||
@@ -0,0 +1,155 @@
|
||||
# Auction House
|
||||
|
||||
---
|
||||
|
||||
The **Auction House** is a global player marketplace where players can list items, bid against each other, and purchase instantly. Combined with a built-in currency system, it creates a full economy for your server.
|
||||
|
||||
---
|
||||
|
||||
## Currency System
|
||||
|
||||
CNPC+ has a **built-in currency** used across all market features.
|
||||
|
||||
- **Fully Configurable** - Name it Coins, Gold, Credits — whatever fits your world. Set a starting balance and max balance cap
|
||||
- **Profile Persistent** - Currency carries across all your character slots
|
||||
- **Vault Support** - Integrates with Bukkit-compatible economy plugins if Vault is installed. Falls back to built-in currency otherwise
|
||||
- **Lifetime Tracking** - Tracks total earned and total spent per player
|
||||
|
||||
> Full scripting API support for currency operations on players.
|
||||
|
||||
---
|
||||
|
||||
## Selling
|
||||
|
||||
List any item with a **Starting Price** for competitive bidding. Optionally set a **Buyout Price** so players can skip the wait and buy immediately. Auctions last **24 hours** by default.
|
||||
|
||||
- A configurable **Listing Fee** is charged when creating an auction
|
||||
- A **Sales Tax** is taken from successful sales
|
||||
- A configurable **minimum listing price** prevents spam listings
|
||||
|
||||
---
|
||||
|
||||
## Bidding
|
||||
|
||||
Compete for items by placing bids. Each bid must meet a **minimum increment** above the current bid (default 5%, configurable by the server).
|
||||
|
||||
- **Snipe Protection** - Auctions extend when last-second bids come in, giving everyone a fair shot
|
||||
- **Auto Refund** - If you get outbid, your currency is automatically returned
|
||||
|
||||
---
|
||||
|
||||
## Buyout
|
||||
|
||||
See something you want right now? Hit **Buy Now** to purchase instantly at the seller's Buyout Price. If you're already the highest bidder, you only pay the **difference** between your bid and the buyout.
|
||||
|
||||
---
|
||||
|
||||
## My Trades
|
||||
|
||||
One screen to manage all your auction activity:
|
||||
|
||||
- **Active Listings** - Items you're currently selling
|
||||
- **Active Bids** - Auctions you're competing in
|
||||
- **Ready to Claim** - Won items and currency waiting for pickup
|
||||
|
||||
You can **cancel** your own listings — items are returned as a claim. If there are active bids, a configurable **cancellation penalty** is charged (default 10%).
|
||||
|
||||
> Claims expire after **20 days**. Collect your items and currency before they're gone!
|
||||
|
||||
---
|
||||
|
||||
## Search & Sorting
|
||||
|
||||
Find items with search and sort tools. Search filters by both **item name** and **seller name** at the same time. Item listings display their **attributes and stats** so you can evaluate items before bidding.
|
||||
|
||||
**Sort Options:**
|
||||
- Newest
|
||||
- Ending Soon
|
||||
- Price (Low to High)
|
||||
- Price (High to Low)
|
||||
- Most Bids
|
||||
|
||||
---
|
||||
|
||||
## Notifications
|
||||
|
||||
Get notified immediately when:
|
||||
- Your item **sells**
|
||||
- You get **outbid**
|
||||
- You **win** an auction
|
||||
- Your auction **expires** with no bids
|
||||
- A **claim is ready** for pickup
|
||||
|
||||
If you're offline, you'll get a full **claim summary** when you log back in.
|
||||
|
||||
---
|
||||
|
||||
## Trade Slots
|
||||
|
||||
Every player gets a limited number of **Trade Slots** (default 8). Slots are shared across Active Listings, Active Bids, **and** Pending Claims — uncollected claims take up space. Pick up your claims to free slots!
|
||||
|
||||
> Server owners can grant more slots via permissions (up to 45) using `customnpcs.auction.trades.X` (where X is the number of slots), or grant **unlimited slots** with `customnpcs.auction.trades.*`.
|
||||
|
||||
---
|
||||
|
||||
## Item Blacklist
|
||||
|
||||
Server owners control what items can be listed on the Auction House.
|
||||
|
||||
**Block by:**
|
||||
- **Item** - Specific registry names like `minecraft:diamond_sword`
|
||||
- **Wildcard** - Patterns like `minecraft:diamond*` to block all diamond items
|
||||
- **Mod** - Block all items from a specific mod
|
||||
- **NBT Tag** - Block items containing certain NBT data
|
||||
|
||||
> **Soulbound** and **Profile-Slotbound** items are always blocked automatically. Bound items cannot be traded.
|
||||
|
||||
**Blacklist Commands:**
|
||||
- `/kam auction blacklist add <item|mod|nbt> <value>` - Add to the blacklist
|
||||
- `/kam auction blacklist remove <item|mod|nbt> <value>` - Remove from the blacklist
|
||||
- `/kam auction blacklist list [item|mod|nbt]` - View blacklisted entries
|
||||
- `/kam auction blacklist reload` - Reload from config
|
||||
- `/kam auction blacklist check` - Check if your held item is blacklisted
|
||||
|
||||
---
|
||||
|
||||
## Auctioneer Role
|
||||
|
||||
A new NPC role that gives players access to the Auction House. Set the Auctioneer role on any NPC and players can interact with them to open the full Auction House — no extra setup needed.
|
||||
|
||||
---
|
||||
|
||||
## Admin Tools
|
||||
|
||||
### Admin Auction GUI
|
||||
|
||||
A management interface for server admins with three tabs:
|
||||
|
||||
- **Listings** - View and search all active auctions. Stop or cancel any listing
|
||||
- **Create** - Create server-managed listings with custom seller names, prices, and durations
|
||||
- **Claims** - Manage the global admin claims pool
|
||||
|
||||
### Commands
|
||||
|
||||
**Player Commands:**
|
||||
- `/kam auction open` - Open the Auction House GUI
|
||||
|
||||
**Admin Commands:**
|
||||
- `/kam auction list [page]` - View active auctions in chat
|
||||
- `/kam auction view <player>` - View a player's auction activity
|
||||
|
||||
> Server owners can enable **auction logging** to track all activity — creates, bids, buyouts, sales, expirations, cancellations, and claims. Each log type can be toggled individually.
|
||||
|
||||
---
|
||||
|
||||
## Scripting API
|
||||
|
||||
All auction events are **cancelable**, letting scripts intercept and control behavior:
|
||||
|
||||
- **CreateEvent** - Before an auction is created
|
||||
- **BidEvent** - Before a bid is placed
|
||||
- **BuyoutEvent** - Before an instant purchase
|
||||
- **CancelEvent** - Before a listing is cancelled
|
||||
- **ClaimEvent** - Before a claim is collected
|
||||
|
||||
---
|
||||
@@ -0,0 +1,96 @@
|
||||
# Bug Fixes
|
||||
|
||||
---
|
||||
|
||||
Fixes to pre-existing systems included in this update.
|
||||
|
||||
---
|
||||
|
||||
## Scripting Engine
|
||||
|
||||
- Fixed instance scripts not loading correctly
|
||||
- Fixed script contexts not initializing properly
|
||||
- Fixed hooks not registering in certain scenarios
|
||||
- Fixed script configuration not syncing between server and client
|
||||
- Fixed Bukkit integration compatibility issues
|
||||
|
||||
---
|
||||
|
||||
## Quests & Dialogs
|
||||
|
||||
- Fixed quest cooldown not working for MC Custom and RL Custom timer types
|
||||
- Fixed quest and dialog modification names not saving correctly
|
||||
- Fixed quest and dialog saving bugs
|
||||
- Fixed dialog and quest category assignment bugs
|
||||
- Fixed bard music restarting or duplicating when opening dialogs
|
||||
|
||||
---
|
||||
|
||||
## NPCs
|
||||
|
||||
- Fixed NPC spawner yaw using camera yaw instead of proper facing direction
|
||||
- Fixed NPC biome spawn settings not persisting after editing
|
||||
- Fixed NPC invisibility caused by request client packet issues
|
||||
- Fixed NPC track speed handling
|
||||
- Fixed NPC set speed edge cases
|
||||
- Fixed missing spawn data on certain NPCs
|
||||
- Fixed block waypoint pathing
|
||||
- Fixed job spawner behavior
|
||||
- Fixed `setRace` not applying correctly
|
||||
- Fixed timer not being callable from a spawning NPC
|
||||
|
||||
---
|
||||
|
||||
## Animations
|
||||
|
||||
- Fixed animation offsets conflicting with vanilla offsets
|
||||
- Fixed animation lookup failures
|
||||
- Fixed animation syncing for built-in animations
|
||||
- Fixed animation resync on teleport
|
||||
- Fixed wrong names being registered on animation registration
|
||||
- Fixed animation controller loading twice on startup
|
||||
|
||||
---
|
||||
|
||||
## GUI
|
||||
|
||||
- Fixed hover labels on scroll components
|
||||
- Fixed text field input handling
|
||||
- Fixed close-on-ESC for outline and tag selection screens
|
||||
- Fixed rendering and selection in fullscreen move systems
|
||||
- Fixed crosshair alignment
|
||||
- Fixed sub-GUI spam closing on rapid clicks
|
||||
- Fixed GUI tab navigation
|
||||
- Fixed panel redirection logic
|
||||
- Fixed player data search functionality
|
||||
- Fixed GL rendering issues
|
||||
|
||||
---
|
||||
|
||||
## Mounts
|
||||
|
||||
- Fixed mount movement jitter by following vanilla horse/boat sync pattern
|
||||
- Fixed mount edge case handling for dismount scenarios
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
- Fixed packet race conditions with language handling
|
||||
- Fixed wrong packet channel and misplaced SideOnly annotations
|
||||
- Fixed categorical assign packets
|
||||
|
||||
---
|
||||
|
||||
## Rendering & Textures
|
||||
|
||||
- Fixed texture pathing removing icons
|
||||
- Fixed rendering glitches in various systems
|
||||
|
||||
---
|
||||
|
||||
## Hitbox
|
||||
|
||||
- Fixed hitbox scaling and collision issues
|
||||
|
||||
---
|
||||
@@ -0,0 +1,88 @@
|
||||
# NPC, Animation & Tool Improvements
|
||||
|
||||
---
|
||||
|
||||
A collection of improvements to NPCs, animations, items, and tools added alongside the major feature updates.
|
||||
|
||||
---
|
||||
|
||||
## Cloner Tab Overhaul
|
||||
|
||||
The Cloner now supports **folders** for organizing your saved NPCs, items, and entries.
|
||||
|
||||
- **Folder System** - Create folders and subfolders to organize the cloner however you like. Works for animations, abilities, forms, auras, outlines, effects, and linked items
|
||||
- **Full Screen Browser** - A new full-width directory view for browsing large collections
|
||||
- **Improved Tabs** - Better tab navigation for switching between cloner categories
|
||||
- **Quick Tags** - Tag-based filtering for fast searching within the cloner
|
||||
|
||||
---
|
||||
|
||||
## Animation Improvements
|
||||
|
||||
- **Data Store** - Animations can store and pass data across events and frames for complex animation logic
|
||||
- **Task System** - A new consumer-based system for reacting to animation lifecycle events
|
||||
- **Expanded API** - More animation control exposed to the scripting API
|
||||
|
||||
---
|
||||
|
||||
## Animated Textures
|
||||
|
||||
Items and effects now support **frame-based animated textures**.
|
||||
|
||||
- **Linked & Scripted Items** - Configure animated textures with custom frame count and frame time
|
||||
- **Custom Effects** - Custom effect icons support animation with the same frame system
|
||||
- **Ability Icons** - Ability icons in the hotbar support animated textures
|
||||
- **Scripted GUI** - Textured rectangles in scripted GUIs support animation
|
||||
- **Configurable** - Set frames per row, ticks per frame, and interpolation between frames
|
||||
|
||||
---
|
||||
|
||||
## NPC API Additions
|
||||
|
||||
New scripting API properties for finer control over NPCs:
|
||||
|
||||
- **Hitbox Data** - Scale NPC hitbox width and height independently via `IHitboxData`
|
||||
- **Tint Data** - Control hurt tint (damage flash) and apply persistent color tints with alpha via `ITintData`
|
||||
- **Behavior Properties** - Sprint, swim, door interaction, fire reactions, tactical chance, and walking range are now scriptable
|
||||
- **Sound Control** - Full sound management via `ISound` with volume, pitch, repeat, and position
|
||||
- **Skin Overlay Colors** - `getColor()` and `setColor()` on skin overlays for tinting overlay layers
|
||||
- **Six Overlays in Singleplayer** - Skin overlays now work properly in singleplayer
|
||||
- **Knockback API** - New methods for applying knockback with support for respecting or ignoring knockback resistance
|
||||
- **Error Reporting** - Script exceptions now report to the Script Console with full stack traces instead of failing silently
|
||||
|
||||
---
|
||||
|
||||
## Natural Spawn Improvements
|
||||
|
||||
The natural NPC spawning system received several improvements:
|
||||
|
||||
- **GUI Improvements** - Better interface for configuring biome-based NPC spawns
|
||||
- **Runtime Control** - Improved runtime management of natural spawns
|
||||
- **Despawn Handling** - Better logic for naturally despawning NPCs when appropriate
|
||||
- **Biome Persistence** - Fixed NPC biome spawn settings not saving after editing
|
||||
|
||||
---
|
||||
|
||||
## NPC Combat & Behavior
|
||||
|
||||
- **Attack Speed** - NPCs now properly respect attack speed when dealing damage, checking elapsed time before allowing hits
|
||||
- **Passive Faction Protection** - NPCs in passive factions are now prevented from taking damage
|
||||
- **Track Speed** - Improved NPC tracking speed handling for smoother target following
|
||||
- **Invalid Texture Fallback** - NPCs with invalid or missing texture paths now gracefully fall back to a default skin instead of showing errors
|
||||
|
||||
---
|
||||
|
||||
## Additional Changes
|
||||
|
||||
- **Player Dialog Events** - Now work the same way as NPC Dialog Events for consistency
|
||||
- **Linked Item Attack Speed** - Added attack speed configuration to linked items
|
||||
- **Linked Item API** - New scripting API methods for linked items
|
||||
- **Right-Click Cycling** - Multi-option NPC buttons (Hair, Eyes, Fur, etc.) can be right-clicked to cycle backwards
|
||||
- **Legacy Quest Import** - Support for importing quest data from legacy CustomNPCs 1.6.4 format
|
||||
- **Fullscreen GUIs** - GUIs extending the CNPC+ base can now be fullscreen and pannable
|
||||
- **Menu Background** - Unified menu background applied across all CNPC+ GUI screens
|
||||
- **Textfield CTRL Operations** - Text fields now support CTRL+A, CTRL+C, CTRL+V, and other standard shortcuts
|
||||
- **Reflection Ban** - `java.lang.reflect` is now banned in scripts for security hardening
|
||||
- **Entity Packet Loss Fix** - Server-side mixin fix for entity packet loss preventing NPCs from becoming invisible
|
||||
|
||||
---
|
||||
@@ -0,0 +1,94 @@
|
||||
# Script Editor & Scripting Enhancements
|
||||
|
||||
---
|
||||
|
||||
The script editor has been rebuilt from the ground up into a **full-featured code editor** right inside Minecraft, with syntax highlighting, autocomplete, error detection, and support for writing scripts in Java.
|
||||
|
||||
---
|
||||
|
||||
## Script Editor
|
||||
|
||||
### Writing Code
|
||||
|
||||
- **Syntax Highlighting** - Keywords, types, variables, methods, strings, and comments are color-coded automatically. Local variables, global fields, and chained method calls each get their own color
|
||||
- **Autocomplete** - Smart suggestions for methods, fields, and classes as you type. Knows whether you're in a static or instance context. Frequently used members appear first, and the cursor is placed between parentheses when a method has parameters
|
||||
- **Auto Imports** - Type a class name and the editor suggests the correct import
|
||||
- **Error Detection** - Catches mistakes before you run your script. Wrong argument types, missing methods, type mismatches, and unused imports are underlined with detailed error messages on hover
|
||||
- **Built-in Globals** - Objects like `Math`, `JSON`, `Date`, and `Number` are recognized automatically in JavaScript mode. Script variables like `npc`, `world`, and `event` are also recognized
|
||||
- **JS Language Support** - Arrow functions (`=>`), shorthand methods, object literals, and array expressions are all understood by the highlighter and type resolver
|
||||
|
||||
> Hover over any symbol to see its **full type information**, documentation, and declaration. Hover tooltips support JSDoc with markdown formatting, headings, code blocks, and scrolling for long documentation.
|
||||
|
||||
---
|
||||
|
||||
### Navigation
|
||||
|
||||
- **Go To Line** - `CTRL+G` jumps to any line
|
||||
- **Search & Replace** - `CTRL+F` to search, `CTRL+H` to replace. All matches highlight in real-time
|
||||
- **Go To Definition** - `CTRL+Click` any method, field, or type to jump to its definition
|
||||
- **Fullscreen Mode** - Expand the editor to fill your entire game window
|
||||
|
||||
---
|
||||
|
||||
### Editing
|
||||
|
||||
- **Undo/Redo** - Smart undo that works on whole words, not individual characters
|
||||
- **Duplicate Line** - `CTRL+D` to duplicate the current line
|
||||
- **Comment Toggle** - `CTRL+/` to comment or uncomment selected lines
|
||||
- **Rename** - Rename a variable or method and every occurrence updates at once
|
||||
- **Move Lines** - Move lines up or down with shortcuts
|
||||
- **Copy Whole Line** - Copy with nothing selected grabs the entire line
|
||||
- **Triple Click** - Select an entire line instantly
|
||||
|
||||
---
|
||||
|
||||
### Visual Polish
|
||||
|
||||
- **Line Numbers** - Clean gutter with current line highlighting
|
||||
- **Smooth Scrolling** - Eased scroll animations
|
||||
- **Indent Guides** - Vertical lines showing nesting depth, with the current scope highlighted in green
|
||||
- **Brace Matching** - Matching `{}` braces light up, unclosed braces turn red
|
||||
- **Smart Brackets** - Typing `{` auto-creates the closing `}` with correct indentation. Same for `()`, `[]`, `""`, and `''`
|
||||
- **Resizable Panels** - Autocomplete and hover panels can be resized and panned
|
||||
- **Keyboard Shortcut Overlay** - Press a button to see all available shortcuts
|
||||
|
||||
---
|
||||
|
||||
## Java Scripting (Janino)
|
||||
|
||||
Write scripts in **real Java** instead of JavaScript. Powered by the **Janino** compiler, your Java code is compiled and runs natively.
|
||||
|
||||
- **Full Java Language** - Classes, interfaces, enums, generics, lambdas, and method references — everything you know from Java
|
||||
- **Automatic Hook Resolution** - Name your methods after the hook you want (e.g., `init`, `tick`, `interact`) and they're wired up automatically
|
||||
- **Language Selector** - Each script tab has a dropdown to choose between JavaScript and Java. Mix and match across tabs
|
||||
- **Broad Support** - Java scripting works in NPC, Player, Forge, Recipe, Effect, and Linked Item scripts
|
||||
- **External Files** - Write `.java` scripts in an external editor and they load automatically
|
||||
|
||||
> Java and JavaScript scripts coexist side by side. Pick whichever language fits your workflow.
|
||||
|
||||
---
|
||||
|
||||
## Client-Side Scripting
|
||||
|
||||
Scripts can now run **on the client** for responsive, visual experiences.
|
||||
|
||||
- **Server Controlled** - Server owners decide whether client scripting is allowed. Players can't enable it themselves
|
||||
- **Automatic Sync** - Script files are sent from the server to all clients on login, and re-synced on script reload
|
||||
- **Safe by Default** - Off by default, must be explicitly enabled in the server config
|
||||
|
||||
> Client-side scripts open the door to custom UI effects, visual feedback, and client-only hooks — all while the server stays in control.
|
||||
|
||||
---
|
||||
|
||||
## Addon API Support
|
||||
|
||||
Addon developers can ship **type definitions** alongside their mods, giving scripters full autocomplete and documentation for addon APIs.
|
||||
|
||||
- **Automatic Loading** - Any mod can include `.d.ts` definition files in their `assets/<modid>/api/` directory and they appear in the script editor automatically
|
||||
- **Patch Support** - Addons can extend existing CNPC+ types with new methods (e.g., a DBC addon adding `getDBCPlayer()` to the Player type)
|
||||
- **Full Documentation** - Parameter names, return types, and descriptions show up in autocomplete and hover info
|
||||
- **Hot Reload** - Definition files are reloaded when Minecraft resources are reloaded
|
||||
|
||||
> Scripters get autocomplete for addon APIs with zero setup. Addon developers just include their definitions.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,40 @@
|
||||
# Trader Enhancements
|
||||
|
||||
---
|
||||
|
||||
Traders have been upgraded with a **stock system** and **currency integration**, turning them into proper shops with limited supply and real economy support.
|
||||
|
||||
---
|
||||
|
||||
## Stock System
|
||||
|
||||
Traders now support **limited stock** with automatic restocking.
|
||||
|
||||
**Stock Modes:**
|
||||
- **Global** - All players share the same stock pool. When an item sells out, it's gone for everyone until restock
|
||||
- **Per-Player** - Each player has their own purchase limits. One player buying out stock doesn't affect others
|
||||
|
||||
**Restock Timers:**
|
||||
- **MC Daily / Weekly** - Restock based on Minecraft time
|
||||
- **RL Daily / Weekly** - Restock based on real-life time
|
||||
- **Custom** - Set your own interval in MC ticks or real-time
|
||||
|
||||
Stock counts, out-of-stock indicators, and restock timers are all visible directly in the trade interface so players know exactly what's available.
|
||||
|
||||
> Admins can manually reset stock or cooldowns via the Trader GUI.
|
||||
|
||||
---
|
||||
|
||||
## Currency Trades
|
||||
|
||||
Trades can now require **currency on top of items**. Combine material costs with coin costs for balanced economy progression.
|
||||
|
||||
For example, a diamond sword might cost 3 diamonds **plus** 500 coins — creating more meaningful economic choices.
|
||||
|
||||
---
|
||||
|
||||
## Balance Preview
|
||||
|
||||
A **client-side balance preview** shows your expected balance during trades, so you can see exactly how much you'll have left before confirming a purchase.
|
||||
|
||||
---
|
||||
Reference in new issue
Block a user