diff --git a/1.11_Market_Update.md b/1.11_Market_Update.md deleted file mode 100644 index 4236e0e63..000000000 --- a/1.11_Market_Update.md +++ /dev/null @@ -1,729 +0,0 @@ -# CustomNPC+ 1.11 Market Update - Implementation Plan - -## Overview - -The 1.11 Market Update introduces a comprehensive economy system for CustomNPC+: - -1. **Currency System** - Built-in CNPC+ currency with optional Vault integration -2. **Trader Enhancements** - Stock system, currency-based trades, reset timers -3. **Auction House** - Global auction system with bidding, buyout, and claims - ---- - -## 1. Currency System - -### Status: COMPLETE ✅ - -The currency system provides a unified economy for CNPC+ features. - -### Features -- **Built-in CNPC+ Currency** - Stored in PlayerData, persists across profile slots -- **Vault Integration** - Optional, when enabled all operations use Vault instead -- **Scripting API** - Full access via `IPlayer` methods -- **Unified Usage** - Both Trader and Auction systems use `PlayerCurrencyData` which auto-delegates to Vault - -### Vault Integration Flow -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PlayerCurrencyData │ -│ │ -│ shouldUseVault() = ConfigMarket.UseVault && VaultUtil.isEnabled()│ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ All methods check shouldUseVault() first: │ │ -│ │ - getBalance() → VaultUtil.getBalance() or local balance │ │ -│ │ - withdraw() → VaultUtil.withdrawMoney() or local │ │ -│ │ - deposit() → VaultUtil.addMoney() or local │ │ -│ │ - canAfford() → VaultUtil.has() or local check │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ Used by: ContainerNPCTrader, AuctionController │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### Key Files -| File | Purpose | -|------|---------| -| `PlayerTradeData.java` | Per-player trade data (currency + auction claims), Vault delegation | -| `IPlayerTradeData.java` | API interface for trade operations (currency + claims) | -| `ConfigMarket.java` | Currency configuration (UseVault, CurrencyName, StartingBalance, MaxBalance) | -| `VaultUtil.java` | Vault API integration via reflection | - -### Configuration (ConfigMarket.java) -``` -Market.Currency: - - Use Vault: false - - Currency Name: "Coins" - - Starting Balance: 0 - - Max Balance: 2147483647 -``` - -### Scripting API (IPlayer) -```javascript -player.getCurrencyBalance() // Get balance -player.setCurrencyBalance(amount) // Set balance -player.depositCurrency(amount) // Add currency (returns success) -player.withdrawCurrency(amount) // Remove currency (returns success) -player.canAffordCurrency(amount) // Check if can afford -player.isUsingVaultCurrency() // Check if Vault is active -player.getFormattedCurrencyBalance() // Get formatted string - -// Via IPlayerData -player.getData().getTradeData() // Get IPlayerTradeData interface -``` - ---- - -## 2. Trader Enhancements - -### Status: COMPLETE ✅ - ---- - -### 2.1 Stock System - COMPLETE ✅ - -Traders can have limited stock that resets on configurable schedules. - -#### Stock Modes -| Mode | Description | Status | -|------|-------------|--------| -| **Global** | All players share the same stock pool | ✅ Working | -| **Per-Player** | Each player has their own stock counts | ✅ Working | - -#### Stock Reset Types (EnumStockReset) -| Type | Description | Status | -|------|-------------|--------| -| `NONE` | Stock never resets | ✅ Working | -| `MCDAILY` | Resets every MC day (24000 ticks) | ✅ Working | -| `MCWEEKLY` | Resets every 7 MC days (168000 ticks) | ✅ Working | -| `MCCUSTOM` | Custom MC tick interval | ✅ Working | -| `RLDAILY` | Resets every 24 real-life hours | ✅ Working | -| `RLWEEKLY` | Resets every 7 real-life days | ✅ Working | -| `RLCUSTOM` | Custom real-time interval | ✅ Working | - -#### Stock UI Features - COMPLETE ✅ -| Feature | Status | Notes | -|---------|--------|-------| -| Stock count per item | ✅ | Shows to right of output slot | -| Out of stock indicator | ✅ | Dark overlay + red count | -| Reset timer countdown | ✅ | Real-time client-side countdown in top-right | -| "Reopen Trader" message | ✅ | Shows when countdown reaches 0 | -| Admin stock configuration | ✅ | SubGuiNpcTraderStock | -| Per-slot stock limits | ✅ | SubGuiNpcTraderStockSlots | -| Reset stock button | ✅ | With confirmation dialog | -| Reset cooldown button | ✅ | With confirmation dialog | - ---- - -### 2.2 Trader Currency Support - COMPLETE ✅ - -Trades can require BOTH items AND currency (additive costs). Uses `PlayerCurrencyData` which auto-delegates to Vault when enabled. - -#### Trade Entry Structure -Each trade slot can have: -- **Sold Item** - What the player receives ✅ -- **Currency 1** - First item cost (existing) ✅ -- **Currency 2** - Second item cost (existing) ✅ -- **Currency Cost** - CNPC+ Currency / Vault cost ✅ NEW - -#### Implementation Details -- Currency cost stored as `long[] currencyCost[18]` in RoleTrader -- Uses `data.tradeData.getBalance()` and `data.tradeData.withdraw()` -- Automatically uses Vault when `ConfigMarket.UseVault` is enabled -- NBT serialization for persistence ✅ - ---- - -### 2.3 Player Trader GUI - COMPLETE ✅ - -#### Display Features -| Element | Position | Status | -|---------|----------|--------| -| "Trader" label | Top-left | ✅ | -| Reset countdown | Top-right | ✅ Real-time ticking | -| Stock count | Right of each trade | ✅ Green/Red | -| Player balance | Bottom-right | ✅ | -| Status message | Bottom-left | ✅ On hover | - ---- - -### 2.4 Multi-Player Sync - COMPLETE ✅ - -MarketRegistry provides linked market synchronization across all NPCs sharing the same market. - ---- - -### 2.5 Scripting API - COMPLETE ✅ - -Full API available via `IRoleTrader` interface. - ---- - -## 3. Auction House System - -### Status: COMPLETE ✅ - -### 3.1 Architecture Overview - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ AuctionController │ -│ - Manages auction listings with player indices │ -│ - Handles listing/bidding/buyout logic │ -│ - Uses ConcurrentHashMap for thread-safety (no locks) │ -│ - Async saves on CNPC+ thread every 30 seconds │ -│ - 30-second update cycle for processing ended auctions │ -└─────────────────────────────────────────────────────────────────┘ - │ - Uses PlayerCurrencyData (Vault-aware) - │ -┌─────────────────────────────────────────────────────────────────┐ -│ PlayerTradeData │ -│ - Combined currency + auction claims (shared across profiles) │ -│ - Currency: Vault integration with local fallback │ -│ - Claims: Thread-safe CopyOnWriteArrayList for async additions │ -│ - Expired claims processed on login │ -│ - Notifications sent immediately to online players │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### 3.2 Vault Integration - -The Auction system uses `PlayerCurrencyData` for all currency operations: -- `AuctionController.createListing()` → `currencyData.withdraw(fee)` -- `AuctionController.placeBid()` → `currencyData.withdraw(bidAmount)` -- `AuctionController.buyout()` → `currencyData.withdraw(buyoutPrice)` -- `AuctionController.claimCurrency()` → `currencyData.deposit(amount)` - -**When Vault is enabled** (`ConfigMarket.UseVault = true`): -- All operations automatically use Vault economy -- No code changes required - `PlayerCurrencyData` handles delegation - -### 3.3 GUI Structure - -``` -GuiAuctionInterface (Base GUI - opened via Auctioneer NPC) - │ - ├── GuiAuctionListing (Browse auctions - PAGE_LISTINGS) - │ - 9x5 grid of auction items - │ - Filter button with tooltip showing current settings - │ - Click item → Opens GuiAuctionBidding - │ - Pagination with up/down arrows - │ - ├── GuiAuctionSell (Create new listing - PAGE_SELL) - │ - Sell slot for item to auction (cloned from inventory) - │ - Click inventory: left=add stack, right=add 1 - │ - Click sell slot: left=clear, right=remove 1 - │ - Starting price + buyout price text fields - │ - Allow Buyout toggle button - │ - Listing fee display - │ - Confirm button with coin icon - │ - ├── GuiAuctionTrades (My Trades - PAGE_CLAIMS) - │ - 9x5 grid showing: active listings, active bids, claims - │ - Blue tint: active listings/bids - │ - Green tint: claimable items - │ - Red tint: refunds - │ - Confirmation system for cancel/claim operations - │ - ├── GuiAuctionBidding (View listing details) - │ - Item display slot - │ - Auction info (seller, price, time, bids) - │ - Bid button → SubGuiAuctionBid - │ - Buy Now button → SubGuiAuctionBuyNow - │ - Own listings: buttons hidden, view-only - │ - ├── SubGuiAuctionBid (Bid confirmation popup) - │ - Shows minimum bid, player balance - │ - Bid amount input field - │ - Place Bid / Cancel buttons - │ - ├── SubGuiAuctionBuyNow (Buyout confirmation popup) - │ - Shows buyout price, player balance - │ - Yes / Cancel buttons - │ - └── SubGuiAuctionSearch (Filter popup) - - Search text input - - Apply/Cancel buttons -``` - -### 3.4 Client Config Sync - -The client uses `AuctionClientConfig` to cache server settings: -- Synced via `LoginPacket` on player join -- Client never uses `ConfigMarket` directly for display -- Ensures server authority over fees, currency name, etc. - -```java -// Server sends config in LoginPacket -AuctionClientConfig.writeToNBT(compound); // Server side - -// Client receives and caches -AuctionClientConfig.readFromNBT(compound); // Client side - -// Client GUI uses cached values -AuctionClientConfig.getListingFee(); -AuctionClientConfig.getCurrencyName(); -AuctionClientConfig.getMinBidIncrement(); -``` - -### 3.5 Data Structures - -#### AuctionListing -```java -public class AuctionListing { - String id; // Unique listing ID (UUID-based) - UUID sellerUUID; // Seller's UUID - String sellerName; // Seller's display name - ItemStack item; // Item being sold - long startingPrice; // Minimum starting bid - long buyoutPrice; // Instant buy price (0 = no buyout) - long currentBid; // Current highest bid - UUID highBidderUUID; // Current high bidder (null if no bids) - String highBidderName; // High bidder display name - int bidCount; // Number of bids placed - long createdTime; // When listing was created - long endTime; // When auction ends - EnumAuctionStatus status; // ACTIVE, ENDED, CANCELLED, CLAIMED -} -``` - -#### AuctionClaim -```java -public class AuctionClaim { - String id; // Unique claim ID - UUID playerUUID; // Player who can claim - String listingId; // Related auction listing - EnumClaimType type; // ITEM, CURRENCY, REFUND - ItemStack item; // Item to claim (if type = ITEM) - long currency; // Currency to claim (if type = CURRENCY/REFUND) - long createdTime; // When claim was created - boolean claimed; // Whether claimed -} -``` - -### 3.6 Security Features - -All actions are validated server-side: - -| Check | Location | Description | -|-------|----------|-------------| -| Own item bid prevention | `placeBid()` | `listing.isSeller(playerUUID)` check | -| Own item buyout prevention | `buyout()` | `listing.isSeller(playerUUID)` check | -| Auction expiry check | `placeBid()/buyout()` | Double-check inside write lock | -| Currency validation | All actions | Verify balance before operations | -| Claim ownership | `claimItem()/claimCurrency()` | `claim.isForPlayer(playerUUID)` check | -| Blacklist check | `createListing()` | `AuctionBlacklist.isBlacklistedForPlayer()` check | -| Bound item check | `createListing()` | Auto-blocks Soulbound/Profile-Slotbound items | - -### 3.7 Snipe Protection - -When a bid is placed with less than `SnipeProtectionMinutes` remaining: -- Extend the auction end time TO `SnipeProtectionMinutes` from now -- Never extend beyond `SnipeProtectionMinutes` -- Default: 2 minutes - -Example: Auction ends in 30 seconds, someone bids → Timer resets to 2:00 - ---- - -### 3.8 Item Blacklist System - -Prevents specific items from being listed on the auction house. - -#### Blacklist Types - -| Type | Description | Example | -|------|-------------|---------| -| **Items** | Block specific items by registry name | `minecraft:diamond_sword` | -| **Wildcards** | Block items matching pattern | `minecraft:diamond*` | -| **Mods** | Block all items from a mod | `customnpcs` | -| **NBT Tags** | Block items with specific NBT | `ench` (enchanted items) | - -#### Auto-Blocked Items (Always Blocked) - -These items are **always** blocked regardless of blacklist settings: -- **Soulbound Items** - Items with `cnpc_soulbind` requirement -- **Profile-Slotbound Items** - Items with `cnpc_profile_slot` requirement - -The system checks for these under `RPGCore/Requirements` in the item's NBT. - -#### Bypass Permission - -Players with OP status or the `customnpcs.auction.blacklist.bypass` permission can list blacklisted items. - -#### Configuration - -``` -Market.Auction.Blacklist: - Enabled: true - BlacklistedItems: ["minecraft:barrier", "minecraft:command_block*"] - BlacklistedMods: [] - BlacklistedNBTTags: [] -``` - -#### Commands - -| Command | Description | -|---------|-------------| -| `/cnpc auction blacklist add ` | Add to blacklist (runtime only) | -| `/cnpc auction blacklist remove ` | Remove from blacklist (runtime only) | -| `/cnpc auction blacklist list [item\|mod\|nbt]` | List blacklisted entries | -| `/cnpc auction blacklist reload` | Reload blacklist from config | -| `/cnpc auction blacklist check` | Check if held item is blacklisted | - ---- - -## 4. AuctionController Performance & Thread Safety - -### 4.1 Threading Model - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ AuctionController │ -│ │ -│ ┌─────────────────────┐ ┌─────────────────┐ ┌───────────────┐ │ -│ │ ConcurrentHashMap │ │ AtomicBoolean │ │ CNPC+ Thread │ │ -│ │ (listings, indices) │ │ dirty, saving │ │ (async saves) │ │ -│ └─────────────────────┘ └─────────────────┘ └───────────────┘ │ -│ │ -│ All operations use ConcurrentHashMap - no explicit locking │ -│ Minecraft is single-threaded for gameplay - inherent safety │ -│ Async saves snapshot data before writing to disk │ -└─────────────────────────────────────────────────────────────────┘ - │ -┌─────────────────────────────────────────────────────────────────┐ -│ PlayerTradeData │ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ Currency: long balance (or Vault delegation) │ │ -│ │ Claims: CopyOnWriteArrayList │ │ -│ │ - Thread-safe for concurrent read/write │ │ -│ │ - Claims can be added from CNPC+ thread for offline players │ │ -│ │ - Stored in PlayerData (persisted with player, not auctions) │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### 4.2 Player Indices for O(1) Lookups - -| Index | Type | Purpose | -|-------|------|---------| -| `playerListingIds` | `Map>` | Active listings by seller | -| `playerBidIds` | `Map>` | Listings where player has active bid | - -**Claims are now stored in PlayerData:** -- `PlayerTradeData.claims` - CopyOnWriteArrayList per player -- Accessed via `PlayerData.get(player).tradeData` -- Shared across all profile slots (combined with currency) - -**Performance Impact:** -- `getPlayerListingCount()`: O(n) → O(1) -- `getPlayerActiveListings()`: O(n) → O(k) where k = player's listings -- `getPlayerActiveBids()`: O(n) → O(k) -- `getPlayerClaims()`: Direct access to player's claims list - -### 4.3 Update Intervals - -| Operation | Interval | Rationale | -|-----------|----------|-----------| -| Process ended auctions | 30 seconds (600 ticks) | Reduces server load | -| Save auction data | 30 seconds (600 ticks) | Batches writes, reduces I/O | -| Process expired claims | On player login | Per-player, not global tick | - -### 4.4 Data Safety - -1. **Currency Deduction** - Withdraw before updating listing -2. **Previous Bidder Refund** - Store info before modification -3. **Concurrent Saves** - `AtomicBoolean` prevents duplicates -4. **Offline Player Claims** - Loaded async on CNPC+ thread, saved to PlayerData -5. **Claim Migration** - Old claims in auction.dat migrated to PlayerData on load - -### 4.5 Server Shutdown Handling - -On `FMLServerStoppedEvent`: -1. `AuctionController.save()` called synchronously -2. Uses CNPC+ thread (single-threaded executor) -3. All pending saves complete before shutdown - ---- - -## 5. File Structure - -### New Files Created - -``` -constants/ - EnumAuctionStatus.java ✅ - EnumAuctionSort.java ✅ - EnumClaimType.java ✅ - EnumNotificationType.java ✅ - -controllers/ - AuctionController.java ✅ (with threading, indices, Vault support) - AuctionConfigSync.java ✅ (server-side config sync utility) - -controllers/data/ - AuctionListing.java ✅ - AuctionClaim.java ✅ - AuctionFilter.java ✅ - AuctionBlacklist.java ✅ (item blacklist checking + bypass) - PlayerTradeData.java ✅ (currency + claims storage per player) - -roles/ - RoleAuctioneer.java ✅ - -containers/ - ContainerAuction.java ✅ - ContainerAuctionListing.java ✅ - ContainerAuctionBidding.java ✅ - ContainerAuctionTrades.java ✅ - ContainerAuctionSell.java ✅ - InventoryAuctionDisplay.java ✅ - SlotAuctionDisplay.java ✅ - -client/ - AuctionClientConfig.java ✅ (client-side config cache) - -client/gui/player/ - GuiAuctionInterface.java ✅ - GuiAuctionListing.java ✅ - GuiAuctionSell.java ✅ - GuiAuctionTrades.java ✅ - GuiAuctionBidding.java ✅ - SubGuiAuctionSearch.java ✅ - SubGuiAuctionBid.java ✅ - SubGuiAuctionBuyNow.java ✅ - AuctionTooltipHandler.java ✅ - -client/gui/util/ - GuiAuctionNavButton.java ✅ - -network/packets/player/ - AuctionActionPacket.java ✅ -``` - -### New Commands - -``` -command/ - AuctionCommand.java ✅ /cnpc auction with list, view, open, blacklist - command/auction/ - BlacklistSubCommand.java ✅ /cnpc auction blacklist subcommands -``` - -### Files Modified - -``` -CustomNpcs.java ✅ Initialize AuctionController, save on shutdown -EnumRoleType.java ✅ Added Auctioneer -EnumGuiType.java ✅ Added PlayerAuction, PlayerAuctionSell, PlayerAuctionTrades, PlayerAuctionBidding -EnumPlayerPacket.java ✅ Added AuctionData, AuctionAction -CommonProxy.java ✅ Handle auction container types -ClientProxy.java ✅ Handle auction GUI types -DataAdvanced.java ✅ Handle Auctioneer role -ConfigMarket.java ✅ Added Auction section -LoginPacket.java ✅ Sync auction config to client -PlayerData.java ✅ Added tradeData field (currency + claims, shared across profiles) -en_US.lang ✅ Added auction lang keys -``` - ---- - -## 6. Configuration - -### ConfigMarket.java Structure - -```java -// ========================================= -// Currency (Market.Currency) -// ========================================= -public static boolean UseVault = false; // Use Vault economy -public static String CurrencyName = "Coins"; // Display name -public static long StartingBalance = 0; // New player balance -public static long MaxBalance = Long.MAX_VALUE; // Balance cap - -// ========================================= -// Auction (Market.Auction) -// ========================================= -public static final int MAX_TRADE_SLOTS = 45; // 9x5 grid maximum -public static final int MIN_TRADE_SLOTS = 1; // Minimum allowed - -public static boolean AuctionEnabled = true; -public static int AuctionDurationHours = 24; // Auction length (hours) -public static long ListingFee = 10; // Fee to create listing -public static long MinimumListingPrice = 1; // Minimum starting price -public static double SalesTaxPercent = 0.05; // 5% tax on sales -public static int DefaultMaxTrades = 8; // Per player (listings + bids + claims) -public static int SnipeProtectionMinutes = 2; // Anti-snipe timer -public static int ClaimExpirationDays = 20; // Days before unclaimed deleted -public static double MinBidIncrementPercent = 0.05; // 5% minimum bid increase - -// ========================================= -// Auction Logging -// ========================================= -public static boolean AuctionLoggingEnabled = false; -public static boolean LogAuctionCreated = true; -public static boolean LogAuctionBid = true; -public static boolean LogAuctionBuyout = true; -public static boolean LogAuctionSold = true; -public static boolean LogAuctionExpired = true; -public static boolean LogAuctionCancelled = true; -public static boolean LogAuctionClaimed = true; - -// ========================================= -// Auction Blacklist (Market.Auction.Blacklist) -// ========================================= -public static boolean BlacklistEnabled = true; -public static String[] BlacklistedItems = {}; // Registry names (supports wildcards) -public static String[] BlacklistedMods = {}; // Mod IDs to block entirely -public static String[] BlacklistedNBTTags = {}; // NBT tag keys to block -``` - -### Auction Permissions - -| Permission | Description | -|------------|-------------| -| `customnpcs.auction.trades.*` | Unlimited trade slots (up to 45) | -| `customnpcs.auction.trades.X` | Exactly X trade slots (where X is 1-45) | -| `customnpcs.auction.blacklist.bypass` | Bypass item blacklist restrictions | - -**Trade Slot Permissions:** -- Default is 8 trade slots -- Permissions only **increase** the limit (permissions below 8 are ignored) -- Maximum is capped at 45 (9x5 grid) -- Trade slots = Active Listings + Active Bids + Pending Claims - -**Blacklist Bypass:** -- OP players or those with `customnpcs.auction.blacklist.bypass` can list blacklisted items -- Soulbound and Profile-Slotbound items are **always** blocked (no bypass) - ---- - -## 7. Commands - -### `/cnpc auction` Command - -Allows interaction with the Auction House without needing an Auctioneer NPC. - -| Subcommand | Usage | Permission | Description | -|------------|-------|------------|-------------| -| `list` | `/cnpc auction list [page]` | OP | View active auctions in chat | -| `view` | `/cnpc auction view ` | OP | View a player's auction activity | -| `open` | `/cnpc auction open` | Any | Opens the Auction House GUI | - -**Examples:** -``` -/cnpc auction list - Show first page of active auctions -/cnpc auction list 2 - Show page 2 of auctions -/cnpc auction view Steve - Show Steve's listings, bids, and claims -/cnpc auction open - Open the full Auction House GUI -``` - -### `/cnpc auction blacklist` Command - -Manage the auction item blacklist (runtime changes, not persisted to config). - -| Subcommand | Usage | Permission | Description | -|------------|-------|------------|-------------| -| `add` | `/cnpc auction blacklist add ` | OP | Add to blacklist | -| `remove` | `/cnpc auction blacklist remove ` | OP | Remove from blacklist | -| `list` | `/cnpc auction blacklist list [item\|mod\|nbt]` | OP | List blacklisted entries | -| `reload` | `/cnpc auction blacklist reload` | OP | Reload from config | -| `check` | `/cnpc auction blacklist check` | Any | Check if held item is blacklisted | - -**Examples:** -``` -/cnpc auction blacklist add item minecraft:diamond_sword -/cnpc auction blacklist add item minecraft:diamond* (wildcard) -/cnpc auction blacklist add mod industrialcraft2 -/cnpc auction blacklist add nbt ench -/cnpc auction blacklist remove item minecraft:diamond_sword -/cnpc auction blacklist list item -/cnpc auction blacklist check (checks held item) -``` - ---- - -## 8. Implementation Status - -### Phase 1: Currency System - COMPLETE ✅ -### Phase 2: Trader Enhancements - COMPLETE ✅ -### Phase 3: Auction Core - COMPLETE ✅ -### Phase 4: Auction GUI - COMPLETE ✅ -### Phase 5: Auction Network - COMPLETE ✅ -### Phase 6: Performance & Safety - COMPLETE ✅ - -1. ~~Threading model with ReentrantReadWriteLock~~ -2. ~~Player indices for O(1) lookups~~ -3. ~~20-second update cycle~~ -4. ~~Async saves on CNPC+ thread~~ -5. ~~Race condition prevention~~ -6. ~~Server shutdown handling~~ -7. ~~Own listing view (buttons hidden)~~ -8. ~~Server-side security checks~~ - -### Remaining Work - -- [ ] **GuiManageAuction (Admin GUI)** - Admin interface to view all auctions, cancel listings, manage claims -- [ ] Additional testing - -### Implemented Since Initial Plan - -- [x] **`/auction` Command** - Console/player command with subcommands: - - `/auction list [page]` - View active auctions from command line - - `/auction view ` - View a player's auction activity (listings, bids, claims) - - `/auction open` - Open Auction House GUI without needing an Auctioneer NPC -- [x] **Permission system integration** - Per-player trade slot limits via `customnpcs.auction.trades.X` permissions - - Default: 8 trade slots - - Maximum: 45 (9x5 grid) - - Synced to client when viewing My Trades - - Cached on login for performance -- [x] **Minimum Listing Price** - Configurable minimum starting price (default: 1) -- [x] **Buyout enabled by default** - Buyout toggle is ON by default when creating listings -- [x] **Search by seller** - Filter can now search by item name OR seller name -- [x] **Rebid prevention** - Players cannot rebid on auctions where they're already the highest bidder -- [x] **Buyout price difference** - When buying out an auction you're bidding on, only pay the difference -- [x] **Immediate claim cleanup** - Claims are removed from storage immediately when claimed -- [x] **Listing cleanup** - Ended listings are removed when all related claims are processed -- [x] **Live notifications** - Notifications sent immediately to online players instead of queued -- [x] **Login claim summary** - Shows detailed claim summary on login (X items and Y currency to claim) -- [x] **PlayerTradeData** - Merged currency + claims into single class (shared across profiles) -- [x] **No locking system** - Pure ConcurrentHashMap for better performance -- [x] **30-second intervals** - Combined process/save cycle for simplicity -- [x] **Async offline player claims** - Claims added to offline players via CNPC+ thread -- [x] **Claim migration** - Old claims in auction.dat automatically migrated to PlayerData -- [x] **Item Blacklist System** - Block items by registry name, mod, NBT tag, or wildcard pattern - - Auto-blocks Soulbound and Profile-Slotbound items (checks `RPGCore/Requirements`) - - Per-player bypass via `customnpcs.auction.blacklist.bypass` permission - - Runtime add/remove commands (not persisted to config) - - Check command to test held item - ---- - -## 9. Design Decisions - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| Claims Storage | In PlayerData, not auction.dat | Claims persist with player, reduces auction.dat size, allows offline claim addition | -| Thread Safety | ConcurrentHashMap (no locks) | Minecraft is single-threaded for gameplay; locks add overhead without benefit | -| Vault Integration | Via PlayerCurrencyData | Single point of delegation, reused by Trader & Auction | -| Update Interval | 30 seconds | Performance vs responsiveness tradeoff | -| Player Indices | ConcurrentHashMap + Sets | O(1) lookups, thread-safe | -| Async Saves | CNPC+ Thread | Doesn't block server tick | -| Offline Claims | Load PlayerData async, add claim, save | Claims can be added to offline players without blocking | -| Config Sync | LoginPacket → AuctionClientConfig | Client never accesses server config directly | -| Item Blacklist | Static utility class | Simple, fast checks without instantiation; auto-blocks bound items | -| Bound Item Detection | Check `RPGCore/Requirements` NBT path | Uses AttributeItemUtil constants for consistency | - ---- - -## 10. Future Considerations (Not in 1.11) - -- Category-based filtering (by item type, mod, etc.) -- Auction history/statistics tracking -- Auction house tax sink (currency removed from economy) -- Reserve prices (hidden minimum price) -- Private auctions (invite-only) -- Watchlist/favorites system -- Price history graphs diff --git a/Ability_Beta_Update.md b/Ability_Beta_Update.md new file mode 100644 index 000000000..8733ebc8b --- /dev/null +++ b/Ability_Beta_Update.md @@ -0,0 +1,144 @@ +# CNPC+ 1.11 Ability System Beta + +🟡 **In Testing** + + + +## Features: ⚒️ + +- **Phase-Based Execution:** + +> All abilities follow a Wind-up → Active → Completion flow. Wind-up lets players react, Active deals the effect. + + + +- **Melee Abilities:** + +> - **Slam** - Leap and AOE on impact. +> - **Heavy Hit** - Single powerful strike with bonus knockback. +> - **Cutter** - Slashing cone arc attack. +> - **Shockwave** - AOE burst radiating from the caster. + + + +- **Energy Orbs:** + +> - **Orb** - Homing energy sphere. +> - **Dual Orb** - Two simultaneous homing orbs. +> - **Orb Barrage** - Rapid volley of orbs in sequence. + + + +- **Energy Discs:** + +> - **Disc** - Spinning flat projectile with optional boomerang. +> - **Dual Disc** - Two simultaneous discs. + + + +- **Energy Beams:** + +> - **Beam** - Sustained homing beam from caster to target. +> - **Dual Beam** - Two simultaneous beams. + + + +- **Laser Shot:** + +> Instant-fire projectile with no homing. Pure aim. + + + +- **Projectile:** + +> Standard projectile using classic Minecraft physics. + + + +- **Movement Abilities:** + +> - **Charge** - Rush forward in a line, damaging everything in path. +> - **Dash** - Quick directional movement without damage. +> - **Teleport** - Instant relocation to the target. + + + +- **AOE Abilities:** + +> - **Sweeper** - Wide rotational sweep around the caster. +> - **Vortex** - Pulls entities inward while dealing damage. + + + +- **Telegraph System:** + +> Visual ground indicators during wind-up. Supports Circle, Ring, Line, Cone, and Point shapes with color customization and warning phase transitions. + + + +- **Energy Projectile Customization:** + +> Modular data system for all energy projectiles: Anchor Point, Display Colors, Homing, Combat Stats, and Lifespan. + + + +- **Ability Effects:** + +> Abilities can apply potion effects on hit: Slowness, Weakness, Poison, Wither, Blindness, Nausea, Hunger, Mining Fatigue. + + + +- **Ability Conditions:** + +> Condition system controlling when abilities can execute based on target state and custom predicates. + + + +- **Interruptible Wind-ups:** + +> Abilities can be interrupted by damage during wind-up, forcing a configurable Dazed recovery state. + + + +- **Movement & Rotation Locking:** + +> Lock caster movement, rotation, or both during Wind-up, Active, or both phases. + + + +- **Animation & Sound Integration:** + +> Wind-up and Active animations with optional duration sync. Custom sound events per phase. + + + +- **Player Abilities:** + +> Players can unlock abilities, select one at a time, and share a universal cooldown. + + + +- **Ability Preview:** + +> Live preview in the ability editor GUI. Test telegraphs, animations, and projectiles on a dummy NPC. + + + +- **Scripting API:** + +> Full event support: Start, Execute, Interrupt, Complete, Hit (modifiable damage/knockback), and Tick events. Start, Execute, and Hit are cancelable. + + + +- **Ability Commands:** + +> `/kam ability list`, `types`, `info`, `delete`, `reload` for managing custom abilities. + + + +**Extras** + +- Global ability presets that update everywhere when edited. +- Weighted ability selection for NPC random usage. +- Configurable min/max range per ability. +- Reusable telegraph presets shared across abilities. diff --git a/Ability_Update.md b/Ability_Update.md new file mode 100644 index 000000000..cf160bc5f --- /dev/null +++ b/Ability_Update.md @@ -0,0 +1,199 @@ +# ⚔️ 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, 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 +- **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 + +> 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 +- **Heavy Hit** - A single powerful melee strike with bonus knockback +- **Cutter** - A slashing arc attack in a cone. Configurable angle and reach +- **Shockwave** - An AOE burst radiating outward from the caster + +> 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. + +### Orbs + +Homing energy spheres that track their target through the air. + +- **Orb** - A single homing projectile +- **Dual Orb** - Fires two orbs simultaneously +- **Orb Barrage** - Fires a volley of orbs in rapid sequence + +### Discs + +Flat spinning projectiles with optional **boomerang** behavior. + +- **Disc** - A single spinning disc with configurable radius and thickness +- **Dual Disc** - Fires two discs simultaneously + +### Beams + +Sustained energy beams that connect the caster to their target. + +- **Beam** - A homing beam with configurable width and head size. Locks the caster in place during channeling +- **Dual Beam** - Fires two beams simultaneously + +### Laser Shot + +An **instant-fire** projectile with configurable width and speed. No homing — pure aim. + +### Projectile + +A standard projectile using classic Minecraft physics for a more traditional ranged attack. + +### 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 +- **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 +- **Lifespan** - Projectile speed, max age, and max travel distance + +--- + +## 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. + +- **Sweeper** - A wide rotational sweep attack around the caster with configurable radius and rotation speed +- **Vortex** - Creates a pulling force that drags nearby entities toward the center while dealing damage + +--- + +## Telegraph System 📍 + +**Telegraphs** are visual ground indicators that warn players before an ability lands. They appear during the wind-up phase and disappear when 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 + +**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 +- **Smooth Interpolation** - Position updates are interpolated for clean visuals + +> 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 +- **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 + +> 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. + +**Available Effects:** +Slowness, Weakness, Poison, Wither, Blindness, Nausea, Hunger, Mining Fatigue + +### Conditions + +Abilities support a **condition system** that controls when they can be used. Conditions are evaluated before execution and can require specific target states, ranges, or custom predicates. + +--- + +## Player Abilities 🧑 + +Players can unlock and use abilities too! + +- **Unlock System** - Players receive abilities via scripting or commands +- **Ability Selection** - Choose one ability at a time from your unlocked list +- **Universal Cooldown** - Shared cooldown across all player abilities + +--- + +## 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 + +> 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, and projectile visuals 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 info ` - View ability details +- `/kam ability delete ` - Delete a custom ability +- `/kam ability reload` - Reload abilities from disk + +--- diff --git a/Ability_Update_Review.md b/Ability_Update_Review.md deleted file mode 100644 index 2ceb38797..000000000 --- a/Ability_Update_Review.md +++ /dev/null @@ -1,301 +0,0 @@ -# Ability System Review - CustomNPC+ - -A comprehensive technical review of the Ability System identifying bugs, logic issues, incomplete features, and inconsistencies. - ---- - -## Critical Issues - -### 1. Cooldown Uses Mixed Time Systems -**Status:** FIXED -**Files:** -- `Ability.java` - Removed cooldown tracking (cooldownEndTime, startCooldown(), isOnCooldown(), canUse()) -- `DataAbilities.java` - Now sole owner of cooldown management using world time - -**Solution:** Removed all cooldown tracking from `Ability.java`. Cooldowns are now managed exclusively by `DataAbilities` using `world.getTotalWorldTime()` for consistency. - ---- - -### 2. AbilityHeal - Heal Over Time Completely Broken -**Status:** FIXED -**File:** `AbilityHeal.java` - -**Solution:** Refactored to separate ally-finding from healing. The `findAlliesInRadius()` method is now called regardless of `instantHeal` flag, populating the list for HoT to use. Added heal particles during HoT ticks. - ---- - -### 3. AbilityGuard - Counter Integration Missing -**Status:** FIXED -**Files:** -- `DataAbilities.java:onDamage()` - Added integration to call `AbilityGuard.onDamageTaken()` -- `DataAbilities.java:getGuardDamageReduction()` - Added helper method for damage reduction - -**Solution:** `DataAbilities.onDamage()` now checks if current ability is an `AbilityGuard` and calls `onDamageTaken()` when the NPC is hit while guarding. - ---- - -## High Priority Issues - -### 4. knockbackUp Stored But Never Applied -**Status:** FIXED -**File:** `Ability.java` - -**Solution:** Updated `applyNpcKnockback()` to accept a `knockbackUp` parameter and use it instead of hardcoded `0.1D`. Both `applyAbilityDamage()` and `applyAbilityDamageWithDirection()` now pass the knockbackUp value from the HitEvent. - ---- - -### 5. Targeting Modes Not Implemented -**Status:** FIXED (Removed) -**File:** `TargetingMode.java` - -**Solution:** Removed the unused targeting modes (`RANDOM_ENEMY`, `LOWEST_HP`, `HIGHEST_THREAT`) from the enum. Only the implemented modes remain: `AGGRO_TARGET`, `SELF`, `AOE_SELF`, `AOE_TARGET`. - ---- - -### 6. Damage Source Inconsistency -**Status:** FIXED -**File:** `Ability.java` - -**Solution:** Standardized both `applyAbilityDamage()` and `applyAbilityDamageWithDirection()` to use `NpcDamageSource("mob", npc)` for consistent damage source handling. - ---- - -## Incomplete Features - -### 7. AbilityProjectile - No Actual Projectile -**Status:** CONFIRMED - INCOMPLETE -**File:** `AbilityProjectile.java:88` - -```java -// TODO: Use custom EntityAbilityProjectile for actual tracking -applyAbilityDamageWithDirection(npc, target, damage, knockback, dx, dz); -``` - -**Current behavior:** -- `speed`, `homing`, `homingStrength` fields exist but are unused -- No projectile entity is spawned -- Damage is instant, just with particle cosmetics - ---- - -### 8. AbilityOrb - No Visual Orb Rendering -**Status:** CONFIRMED - INCOMPLETE -**File:** `AbilityOrb.java:117-179` - -**Problem:** The orb tracks position (`orbX`, `orbY`, `orbZ`) with runtime getters but nothing renders it client-side. Players only see particle effects on hit, not the flying orb itself. - ---- - -### 9. AbilityBeam - No Visual Beam Rendering -**Status:** CONFIRMED - INCOMPLETE -**File:** `AbilityBeam.java:213-220` - -**Problem:** Only spawns flame particles along the path. No actual beam entity or proper beam visual. The `lockOnTarget` telegraph during active phase isn't visually communicated. - ---- - -## Medium Priority Issues - -### 10. Hazard/Trap Telegraph Offset Mismatch -**Status:** CONFIRMED -**Files:** -- `AbilityHazard.java:119-147, 201-248` -- `AbilityTrap.java:109-138, 166-205` - -**Problem:** Telegraph is created at target position, but actual effect applies random offset: - -```java -// createTelegraph() - No offset applied, shows at target position - -// onExecute() - Random offset applied AFTER telegraph locks -double[] pos = calculateOffsetPosition(telegraph.getX(), telegraph.getY(), telegraph.getZ()); -zoneX = pos[0]; // Actual effect position differs from telegraph -``` - -**Impact:** Players see telegraph at one location, damage happens elsewhere. Misleading visual feedback. - -**Fix:** Either apply offset during telegraph creation, or update telegraph position in `onExecute()`. - ---- - -### 11. AbilityCharge Telegraph Direction Timing -**Status:** CONFIRMED - MINOR -**File:** `AbilityCharge.java:91, 254-286` - -**Problem:** Telegraph created at tick 0, direction locked at tick 1: - -```java -// Telegraph created during setup (tick 0) with current target direction -// Line 91: Direction locked at tick 1 -if (tick == 1) { - lockChargeDirection(npc, target); -} -``` - -**Impact:** If target moves between tick 0-1, telegraph direction won't match actual charge. - ---- - -### 12. AbilitySlam - activeTicks May End Before Landing -**Status:** CONFIRMED - MINOR -**File:** `AbilitySlam.java:52-53, 230-250` - -**Problem:** `activeTicks = 60` is fixed, but if NPC takes longer to land, ability transitions to RECOVERY without `onLanding()` being called. - -**Secondary issue:** If NPC lands early (tick 15), active phase continues until tick 60 doing nothing - wasted ticks. - ---- - -### 13. FOLLOW Mode Telegraph Not Updated -**Status:** CONFIRMED -**Files:** `AbilityHazard.java:139-146`, `AbilityVortex.java` - -**Problem:** For `FOLLOW_TARGET` mode, telegraph locks position on execute, but the actual hazard zone follows the target. Visual mismatch between warning and danger. - ---- - -## Redundancies - -### 14. Duplicate findGroundLevel() Methods -**Status:** FIXED -**Files:** -- `Ability.java` - Made `findGroundLevel()` public static -- `TelegraphInstance.java` - Now uses `Ability.findGroundLevel()` - ---- - -### 15. Duplicate calculateOffsetPosition() Methods -**Status:** FIXED -**Files:** -- `Ability.java` - Added shared static `calculateOffsetPosition()` method -- `AbilityTrap.java` - Removed duplicate, uses `Ability.calculateOffsetPosition()` -- `AbilityHazard.java` - Removed duplicate, uses `Ability.calculateOffsetPosition()` - ---- - -### 16. Unused applyKnockback in AbilityOrb -**Status:** FIXED -**File:** `AbilityOrb.java` - -Removed the unused `applyKnockback()` method. - ---- - -### 17. Duplicate Reset/Interrupt Cleanup -**Status:** NOT FIXED (Low Priority) -**Multiple ability files** - -Most abilities override both `reset()` and `onInterrupt()` with duplicate cleanup code. Pattern should have `onInterrupt()` call `reset()` or use shared cleanup method. - ---- - -## Minor Issues & Inconsistencies - -### 18. Field Naming Confusion -- `AbilityHazard`: `damagePerSecond` with `damageInterval` - name suggests per-second but applied every N ticks -- `AbilityCharge`: `hitWidth` stored as `hitRadius` in older NBT (backward compat exists) - -### 19. AbilityVortex Entity Lookup Performance -**File:** `AbilityVortex.java:201-212` - -Iterates through ALL loaded entities every tick to find pulled entities by UUID. Should cache entity references or use entity IDs. - -### 20. Telegraph Yaw Rotation -**File:** `TelegraphRenderer.java:95` - -Uses `GL11.glRotatef(-instance.getYaw(), 0, 1, 0)` - negative yaw. Should verify this matches Minecraft's yaw conventions. - -### 21. Missing Null Checks -Various locations where `target` could be null but is used without defensive checks: -- `AbilityProjectile.java:73` -- `AbilityOrb.java:138-171` - ---- - -## Missing Features (Enhancement Requests) - -These are not bugs but notable missing functionality: - -1. **No Ability Chaining/Combo System** - No way to trigger abilities from other abilities -2. **No Cooldown Reduction on Hit** - Can't reduce cooldowns based on successful hits -3. **No Multi-Target Hit Limit** - AOE abilities hit unlimited targets (unlike Vortex's `maxTargets`) -4. **No Damage Type Configuration** - All abilities use mob/melee damage, no magic/fire/etc options -5. **No Target Validation During Execution** - If target dies/despawns mid-ability, behavior varies - ---- - -## Priority Summary - -| Priority | Issue | Status | -|----------|-------|--------| -| **CRITICAL** | Cooldown time system mismatch | FIXED | -| **CRITICAL** | AbilityHeal HoT broken | FIXED | -| **CRITICAL** | AbilityGuard counter not integrated | FIXED | -| **HIGH** | knockbackUp ignored | FIXED | -| **HIGH** | Targeting modes unimplemented | FIXED (Removed) | -| **HIGH** | Damage source inconsistency | FIXED | -| **MEDIUM** | Projectile/Orb/Beam no visuals | Not Fixed | -| **MEDIUM** | Hazard/Trap telegraph offset | Not Fixed | -| **LOW** | Charge telegraph timing | Not Fixed | -| **LOW** | Slam activeTicks timing | Not Fixed | -| **LOW** | Code redundancies | FIXED | - ---- - -## Remaining Issues - -The following issues were not addressed in this fix round: - -1. **Projectile/Orb/Beam Visual Rendering** - These abilities still only use particles, no actual entity rendering -2. **Hazard/Trap Telegraph Offset Mismatch** - Telegraph shows at target, effect spawns with offset -3. **Charge Telegraph Direction Timing** - Minor visual mismatch between tick 0-1 -4. **Slam activeTicks Timing** - Fixed at 60 ticks regardless of landing time - ---- - -## Changes Made - -### Ability.java -- Removed `cooldownEndTime` transient field -- Removed `startCooldown()`, `isOnCooldown()`, `canUse()` methods (cooldown managed by DataAbilities) -- Updated `tick()` to not call startCooldown (DataAbilities handles it) -- Updated `reset()` to not reset cooldownEndTime -- Made `findGroundLevel()` public static for shared use -- Added static `calculateOffsetPosition()` utility method -- Fixed `applyNpcKnockback()` to accept and use knockbackUp parameter -- Standardized damage source to `NpcDamageSource("mob", npc)` in both damage methods -- Removed deprecated getters/setters (telegraphColor, castSound, animationId) - -### IAbility.java (API) -- Removed `isOnCooldown()` method from interface - -### DataAbilities.java -- Added AbilityGuard import -- Added guard counter integration in `onDamage()` -- Added `getGuardDamageReduction()` helper method -- Removed deprecated `isMovementBlocked()` method -- Updated knockbackUp javadoc (no longer deprecated) - -### CombatHandler.java -- Renamed `isMovementBlocked()` to `isAbilityControllingMovement()` - -### TargetingMode.java -- Removed unused modes: `RANDOM_ENEMY`, `LOWEST_HP`, `HIGHEST_THREAT` - -### AbilityHeal.java -- Fixed HoT by separating `findAlliesInRadius()` from healing logic -- Allies are now found in `onExecute()` regardless of instantHeal flag -- Added heal particles during HoT ticks - -### AbilityHazard.java -- Removed duplicate `calculateOffsetPosition()`, uses `Ability.calculateOffsetPosition()` -- Removed deprecated `getDamagePerTick()`/`setDamagePerTick()` methods - -### AbilityTrap.java -- Removed duplicate `calculateOffsetPosition()`, uses `Ability.calculateOffsetPosition()` - -### AbilityOrb.java -- Removed unused `applyKnockback()` method - -### TelegraphInstance.java -- Removed duplicate `findGroundLevel()`, uses `Ability.findGroundLevel()` diff --git a/Auction_Beta_Update.md b/Auction_Beta_Update.md new file mode 100644 index 000000000..8b382b4af --- /dev/null +++ b/Auction_Beta_Update.md @@ -0,0 +1,57 @@ +# CNPC+ 1.11 Beta - Market Update + +🟡 **In Testing** + +## Features: ⚒️ + +- **Built-in Currency System:** +> Fully configurable economy currency used across all Market features. Profile persistent across character slots with Vault support for Bukkit-compatible plugins. + +- **Auction House:** +> Global player marketplace for listing items, bidding, and instant buyouts. Auctions last 24 hours by default. + +- **Auction Bidding & Snipe Protection:** +> Competitive bidding with a 5% minimum bid increment. Snipe Protection extends auctions on last-second bids. Outbid players are automatically refunded. + +- **Buyout System:** +> Instant purchase at the seller's Buyout Price. Highest bidder only pays the difference between their bid and the buyout. + +- **My Trades:** +> Single screen for managing Active Listings, Active Bids, and Pending Claims. Players can cancel listings with a 10% penalty if bids exist. + +- **Auction Notifications:** +> Real-time notifications for sales, outbids, and wins. Offline players receive a claim summary on login. + +- **Trade Slots:** +> Limited slots (default 8, max 45) shared across listings, bids, and claims. Configurable via permissions. + +- **Claim Expiration:** +> Uncollected claims expire after 20 days. + +- **Item Blacklist:** +> Block items by registry name, wildcard pattern, mod, or NBT tag. Soulbound and Profile-Slotbound items are always blocked automatically. + +- **Listing Fee & Sales Tax:** +> Configurable listing fee charged on creation and sales tax taken from successful sales. + +- **Search & Sorting:** +> Search by item name or seller. Sort by newest, ending soon, price, or most bids. + +- **Trader Stock System:** +> Limited stock with Global or Per-Player modes. Restock timers for MC Daily/Weekly, RL Daily/Weekly, or Custom intervals. + +- **Trader Currency Trades:** +> Trades can require currency on top of item costs. + +- **Auctioneer NPC Role:** +> New role that opens the Auction House on interaction. No configuration needed. + +- **Auction Commands:** +> `/kam auction open` for players. Admin commands for listing view and player activity inspection. + +**Extras** + +- Auction logging for server admins (tracks creates, bids, buyouts, sales, expirations, cancellations, claims). +- Lifetime currency tracking per player (total earned and spent). +- Configurable starting balance and max balance cap. +- Blacklist bypass permission for admins. diff --git a/Auction_Update.md b/Auction_Update.md index ae839832f..a04cc12e9 100644 --- a/Auction_Update.md +++ b/Auction_Update.md @@ -10,9 +10,10 @@ The **Market Update** brings a complete economy system to CNPC+! Create bustling CNPC+ now has a **built-in currency** used across all Market features! -- **Fully Configurable** - Name it Coins, Gold, Credits, whatever fits your world +- **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** - CNPC+ integrates seamlessly with Bukkit-compatible economy plugins if Vault is installed +- **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. @@ -20,17 +21,17 @@ CNPC+ now has a **built-in currency** used across all Market features! ## Auction House 🏛️ -A **global player marketplace** where players can list items, bid against each other, and purchase instantly! +A **global player marketplace** where players can list items, bid against each other, and purchase instantly! Search for items by name or seller, and sort by newest, ending soon, price, or most bids. ### 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. +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. > 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! **Snipe Protection** extends auctions when last-second bids come in, giving everyone a fair shot. If you get outbid, your currency is **automatically refunded**. +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 @@ -44,7 +45,9 @@ One screen to manage all your auction activity: - **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. +Players can **cancel** their own listings -- items are returned as a claim. If there are active bids on the listing, a **10% cancellation penalty** is charged. + +> Claims expire after **20 days**. Make sure to collect your items and currency before they're gone! ### Notifications @@ -52,7 +55,7 @@ Get notified immediately when your item sells, when you're outbid, or when you w ### Trade Slots -Each player has a limited number of **Trade Slots** (default 8). Trade Slots are shared between Active Listings, Active Bids, and Pending Claims. +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. @@ -68,14 +71,9 @@ Server owners have full control over what items can be listed on the Auction Hou - **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! +> **Soulbound** and **Profile-Slotbound** items are always blocked automatically, regardless of blacklist settings. Bound items cannot be traded! -**Blacklist Commands:** -- `/kam auction blacklist add ` - Add entry -- `/kam auction blacklist remove ` - Remove entry -- `/kam auction blacklist list` - View all blacklisted entries -- `/kam auction blacklist check` - Check if your held item is blacklisted -- `/kam auction blacklist reload` - Reload from config +Manage the blacklist in-game with `/kam auction blacklist` commands, or configure it directly in the config file. --- @@ -94,14 +92,10 @@ Traders now support **limited stock** with automatic restocking! - **RL Daily / Weekly** - Restock based on real-life time - **Custom** - Set your own interval in MC ticks or real-time -**Visual Feedback:** -- Stock count displayed next to each trade -- **Out of Stock** overlay when unavailable -- Live **countdown timer** until next restock +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! @@ -110,10 +104,9 @@ Trades can now require **currency on top of items**. Combine material costs with ## 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 configuration needed +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. -### Auction Command +### Auction Commands Players can also access the Auction House via command: @@ -123,5 +116,6 @@ Players can also access the Auction House via command: - `/kam auction list [page]` - View active auctions in chat - `/kam auction view ` - 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). +--- diff --git a/CONCEPT_FOR_TABS.png b/CONCEPT_FOR_TABS.png deleted file mode 100644 index 17a02be71..000000000 Binary files a/CONCEPT_FOR_TABS.png and /dev/null differ diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java index 5a13cf917..03faf42d5 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -138,6 +138,16 @@ public abstract class Ability implements IAbility { // OPTIONAL OVERRIDES // ═══════════════════════════════════════════════════════════════════ + /** + * Returns true if this ability type deals damage. + * Override in non-damaging subclasses to return false. + * Used by external mods to determine whether to show + * damage-related configuration (e.g., DBC stats tab). + */ + public boolean hasDamage() { + return true; + } + public void onWindUpTick(EntityLivingBase caster, EntityLivingBase target, World world, int tick) { } @@ -247,12 +257,22 @@ public abstract class Ability implements IAbility { // Apply damage if (damage > 0) { - if (caster instanceof EntityNPCInterface) { - hitEntity.attackEntityFrom(new NpcDamageSource("mob", (EntityNPCInterface) caster), damage); - } else if (caster instanceof EntityPlayer) { - hitEntity.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) caster), damage); - } else { - hitEntity.attackEntityFrom(DamageSource.causeMobDamage(caster), damage); + // Check for external damage handler first (e.g., DBC Addon) + IAbilityDamageHandler handler = AbilityController.Instance.getDamageHandler(); + boolean handled = false; + if (handler != null) { + handled = handler.handleDamage(this, caster, hitEntity, damage, knockback, knockbackUp, + knockbackDirX, knockbackDirZ); + } + if (!handled) { + // Default damage path + if (caster instanceof EntityNPCInterface) { + hitEntity.attackEntityFrom(new NpcDamageSource("mob", (EntityNPCInterface) caster), damage); + } else if (caster instanceof EntityPlayer) { + hitEntity.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) caster), damage); + } else { + hitEntity.attackEntityFrom(DamageSource.causeMobDamage(caster), damage); + } } } @@ -483,6 +503,11 @@ public abstract class Ability implements IAbility { } } + // External field providers (e.g., DBC Addon injecting a "DBC" tab) + for (IAbilityFieldProvider provider : AbilityController.Instance.getFieldProviders()) { + provider.addFieldDefinitions(this, defs); + } + return defs; } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java index f12ebd421..213c1a214 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java @@ -39,6 +39,12 @@ public class AbilityController implements IAbilityHandler { * Used by AbilitySlot for cache invalidation. */ private int version = 0; + /** External damage handler (e.g., DBC Addon). */ + private IAbilityDamageHandler damageHandler = null; + + /** External field providers for ability GUI (e.g., DBC Addon). Client-side only. */ + private final List fieldProviders = new ArrayList<>(); + public AbilityController() { registerBuiltinTypes(); } @@ -386,4 +392,39 @@ public class AbilityController implements IAbilityHandler { registerType("cnpc:hazard", AbilityHazard::new); registerType("cnpc:trap", AbilityTrap::new); } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXTERNAL DAMAGE HANDLER + // ═══════════════════════════════════════════════════════════════════════════ + + /** + * Register an external damage handler for abilities. + * When set, the handler is called before default damage application. + * If the handler returns true, default damage is skipped. + */ + public void registerDamageHandler(IAbilityDamageHandler handler) { + this.damageHandler = handler; + } + + /** Get the registered damage handler, or null if none. */ + public IAbilityDamageHandler getDamageHandler() { + return damageHandler; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXTERNAL FIELD PROVIDERS + // ═══════════════════════════════════════════════════════════════════════════ + + /** + * Register an external field provider for ability GUIs. + * Providers are called during getAllDefinitions() to inject custom tabs/fields. + */ + public void registerFieldProvider(IAbilityFieldProvider provider) { + fieldProviders.add(provider); + } + + /** Get all registered field providers. */ + public List getFieldProviders() { + return fieldProviders; + } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityDamageHandler.java b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityDamageHandler.java new file mode 100644 index 000000000..9a7f353c1 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityDamageHandler.java @@ -0,0 +1,27 @@ +package kamkeel.npcs.controllers.data.ability; + +import net.minecraft.entity.EntityLivingBase; + +/** + * External damage handler for abilities. + * When registered, called AFTER script events but BEFORE default attackEntityFrom. + * If handleDamage returns true, default damage is skipped. + */ +public interface IAbilityDamageHandler { + /** + * Handle damage from an ability. + * + * @param ability The ability dealing damage + * @param caster The entity executing the ability + * @param target The entity being hit + * @param damage The damage amount (after script event modifications) + * @param knockback The horizontal knockback + * @param knockbackUp The vertical knockback + * @param knockbackDirX The X component of knockback direction + * @param knockbackDirZ The Z component of knockback direction + * @return true if damage was handled (skip default), false to use default damage + */ + boolean handleDamage(Ability ability, EntityLivingBase caster, EntityLivingBase target, + float damage, float knockback, float knockbackUp, + double knockbackDirX, double knockbackDirZ); +} diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityFieldProvider.java b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityFieldProvider.java new file mode 100644 index 000000000..f196b307b --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/IAbilityFieldProvider.java @@ -0,0 +1,25 @@ +package kamkeel.npcs.controllers.data.ability; + +import noppes.npcs.client.gui.builder.FieldDef; + +import java.util.List; + +/** + * External field definition provider for abilities. + * Called during getAllDefinitions() to allow mods to inject custom tabs/fields + * into the ability configuration GUI. + *

+ * Note: Although this interface references FieldDef (client-only), it is safe to + * register on both sides because the generic type is erased at bytecode level. + * The addFieldDefinitions method is only ever called from client-side GUI code. + */ +public interface IAbilityFieldProvider { + /** + * Add field definitions to the ability's GUI. + * Fields added here should use .tab() to assign them to a custom tab. + * + * @param ability The ability being configured + * @param defs The mutable list of field definitions to add to + */ + void addFieldDefinitions(Ability ability, List defs); +} diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java index c8889dfe9..5157eea01 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeam.java @@ -109,6 +109,7 @@ public class AbilityBeam extends Ability implements IAbilityEnergyBeam { beamEntity.setupCharging(anchorData, windUpTicks, offsetDist); beamEntity.setEffects(this.effects); + beamEntity.setSourceAbility(this); world.spawnEntityInWorld(beamEntity); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeamDual.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeamDual.java index 56c24e791..762145fc4 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeamDual.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityBeamDual.java @@ -137,6 +137,8 @@ public class AbilityBeamDual extends Ability { beamEntity.setEffects(this.effects); if (dualFire) beamEntity2.setEffects(this.effects); + beamEntity.setSourceAbility(this); + beamEntity2.setSourceAbility(this); world.spawnEntityInWorld(beamEntity); world.spawnEntityInWorld(beamEntity2); } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java index 10cfd2978..1a1e76aa3 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDash.java @@ -111,6 +111,11 @@ public class AbilityDash extends Ability implements IAbilityDash { this.activeSound = "mob.endermen.portal"; } + @Override + public boolean hasDamage() { + return false; + } + @Override public boolean isTargetingModeLocked() { return true; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java index 0bbad4209..0f91869fa 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDisc.java @@ -110,6 +110,7 @@ public class AbilityDisc extends Ability implements IAbilityDisc { discEntity.setupCharging(anchorData, windUpTicks); discEntity.setEffects(this.effects); + discEntity.setSourceAbility(this); world.spawnEntityInWorld(discEntity); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDiscDual.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDiscDual.java index 0dea0a072..421861fcd 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDiscDual.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityDiscDual.java @@ -146,6 +146,8 @@ public class AbilityDiscDual extends Ability { discEntity2.setEffects(this.effects); } + discEntity.setSourceAbility(this); + discEntity2.setSourceAbility(this); world.spawnEntityInWorld(discEntity); world.spawnEntityInWorld(discEntity2); } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java index eab84d138..ec9f157f9 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java @@ -65,6 +65,11 @@ public class AbilityGuard extends Ability implements IAbilityGuard { this.counterAnimationName = "Ability_Guard_Counter"; } + @Override + public boolean hasDamage() { + return false; + } + public enum CounterType { FLAT, PERCENT; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java index 107db40c6..c1faf000b 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHeal.java @@ -58,6 +58,11 @@ public class AbilityHeal extends Ability implements IAbilityHeal { this.showTelegraph = false; } + @Override + public boolean hasDamage() { + return false; + } + @Override public boolean isTargetingModeLocked() { return true; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java index 48fddd63c..4be2315d5 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityLaserShot.java @@ -99,6 +99,7 @@ public class AbilityLaserShot extends Ability implements IAbilityLaserShot { laserEntity.setupCharging(anchorData, windUpTicks); laserEntity.setEffects(this.effects); + laserEntity.setSourceAbility(this); world.spawnEntityInWorld(laserEntity); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java index 519fef749..96881cf4e 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrb.java @@ -104,6 +104,7 @@ public class AbilityOrb extends Ability implements IAbilityOrb { orbEntity.setupCharging(anchorData, windUpTicks); orbEntity.setEffects(this.effects); + orbEntity.setSourceAbility(this); world.spawnEntityInWorld(orbEntity); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java index be323014b..71be07a34 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java @@ -129,6 +129,7 @@ public class AbilityOrbBarrage extends Ability { ); orbEntity.setupCharging(anchorData[i], windUpTicks); + orbEntity.setSourceAbility(this); chargingEntities[i] = orbEntity; world.spawnEntityInWorld(orbEntity); @@ -179,6 +180,7 @@ public class AbilityOrbBarrage extends Ability { orbSize, colorData, combatData, homingData, lightningData, lifespanData, trajectoryData ); + orb.setSourceAbility(this); world.spawnEntityInWorld(orb); orbEntities.add(orb); diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbDual.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbDual.java index 3fdd5eda6..bd2870438 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbDual.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbDual.java @@ -133,6 +133,8 @@ public class AbilityOrbDual extends Ability { orbEntity2.setEffects(this.effects); } + orbEntity.setSourceAbility(this); + orbEntity2.setSourceAbility(this); world.spawnEntityInWorld(orbEntity); world.spawnEntityInWorld(orbEntity2); } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java index 887ef25fc..cb06b7b6b 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTeleport.java @@ -84,6 +84,11 @@ public class AbilityTeleport extends Ability implements IAbilityTeleport { this.activeSound = "mob.endermen.portal"; } + @Override + public boolean hasDamage() { + return false; + } + @Override public boolean isTargetingModeLocked() { return true; diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java index fce06741b..7080ce2ef 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java @@ -4,8 +4,11 @@ import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import io.netty.buffer.ByteBuf; +import kamkeel.npcs.controllers.data.ability.Ability; +import kamkeel.npcs.controllers.data.ability.AbilityController; import kamkeel.npcs.controllers.data.ability.AbilityEffect; import kamkeel.npcs.controllers.data.ability.AnchorPoint; +import kamkeel.npcs.controllers.data.ability.IAbilityDamageHandler; import kamkeel.npcs.controllers.data.ability.data.*; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; @@ -77,6 +80,9 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP protected boolean previewMode = false; // Client-side preview mode (no damage/effects) protected EntityLivingBase previewOwner = null; // Direct reference for GUI preview (no world lookup) + /** The ability that spawned this projectile. Transient, not saved to NBT. */ + protected transient Ability sourceAbility = null; + // ==================== ROTATION INTERPOLATION ==================== public float prevRotationValX, prevRotationValY, prevRotationValZ; public float rotationValX, rotationValY, rotationValZ; @@ -322,14 +328,30 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP if (previewMode) return; // Skip damage in preview mode Entity owner = getOwnerEntity(); - if (owner instanceof EntityNPCInterface) { - target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), dmg); - } else if (owner instanceof EntityPlayer) { - target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) owner), dmg); - } else if (owner instanceof EntityLivingBase) { - target.attackEntityFrom(DamageSource.causeMobDamage((EntityLivingBase) owner), dmg); - } else { - target.attackEntityFrom(new NpcDamageSource("npc_ability", null), dmg); + // Check for external damage handler (e.g., DBC Addon) + boolean handled = false; + if (sourceAbility != null && owner instanceof EntityLivingBase) { + IAbilityDamageHandler handler = AbilityController.Instance.getDamageHandler(); + if (handler != null) { + double dx = target.posX - posX; + double dz = target.posZ - posZ; + float kbUp = getKnockbackUp() > 0 ? getKnockbackUp() : 0.1f; + handled = handler.handleDamage(sourceAbility, (EntityLivingBase) owner, target, + dmg, kb, kbUp, dx, dz); + } + } + + if (!handled) { + // Default damage path + if (owner instanceof EntityNPCInterface) { + target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), dmg); + } else if (owner instanceof EntityPlayer) { + target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) owner), dmg); + } else if (owner instanceof EntityLivingBase) { + target.attackEntityFrom(DamageSource.causeMobDamage((EntityLivingBase) owner), dmg); + } else { + target.attackEntityFrom(new NpcDamageSource("npc_ability", null), dmg); + } } if (kb > 0) { @@ -361,6 +383,21 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP this.effects = effects != null ? effects : new ArrayList<>(); } + /** + * Set the source ability that spawned this projectile. + * Used by external damage handlers to access ability-specific data. + */ + public void setSourceAbility(Ability ability) { + this.sourceAbility = ability; + } + + /** + * Get the source ability that spawned this projectile. + */ + public Ability getSourceAbility() { + return sourceAbility; + } + protected void doExplosion() { if (previewMode) return; // Skip explosion in preview mode worldObj.playSoundEffect(posX, posY, posZ, "random.explode", 1.0f, 1.0f); diff --git a/src/main/java/kamkeel/npcs/network/packets/data/LoginPacket.java b/src/main/java/kamkeel/npcs/network/packets/data/LoginPacket.java index 2717d6277..75e0c40ae 100644 --- a/src/main/java/kamkeel/npcs/network/packets/data/LoginPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/data/LoginPacket.java @@ -16,7 +16,9 @@ import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.client.AuctionClientConfig; import noppes.npcs.client.ClientCacheHandler; import noppes.npcs.config.ConfigMain; +import noppes.npcs.client.ScriptClientConfig; import noppes.npcs.controllers.AuctionConfigSync; +import noppes.npcs.controllers.ScriptConfigSync; import java.io.IOException; import java.util.EnumMap; @@ -65,6 +67,11 @@ public final class LoginPacket extends AbstractPacket { NBTTagCompound auctionConfig = new NBTTagCompound(); AuctionConfigSync.writeToNBT(auctionConfig); ByteBufUtils.writeNBT(out, auctionConfig); + + // Send script config (server-side sync) + NBTTagCompound scriptConfig = new NBTTagCompound(); + ScriptConfigSync.writeToNBT(scriptConfig); + ByteBufUtils.writeNBT(out, scriptConfig); } @SideOnly(Side.CLIENT) @@ -88,6 +95,10 @@ public final class LoginPacket extends AbstractPacket { NBTTagCompound auctionConfig = ByteBufUtils.readNBT(in); AuctionClientConfig.readFromNBT(auctionConfig); + // Read script config + NBTTagCompound scriptConfig = ByteBufUtils.readNBT(in); + ScriptClientConfig.readFromNBT(scriptConfig); + PacketClient.sendClient(new SyncRevisionInfoPacket( serverKey, ClientCacheHandler.getLastServerKey(), diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/ScriptFilesPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/ScriptFilesPacket.java index 700513d78..d025fcb26 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/ScriptFilesPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/ScriptFilesPacket.java @@ -13,6 +13,7 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.CustomNpcsPermissions; import noppes.npcs.NBTTags; +import noppes.npcs.client.ScriptClientConfig; import noppes.npcs.config.ConfigScript; import noppes.npcs.controllers.ScriptController; @@ -69,8 +70,8 @@ public final class ScriptFilesPacket extends AbstractPacket { } //Set client side configs - ConfigScript.ScriptingEnabled = compound.getBoolean("ScriptingEnabled"); - ConfigScript.RunLoadedScriptsFirst = compound.getBoolean("LoadedFirst"); + ScriptClientConfig.setScriptingEnabled(compound.getBoolean("ScriptingEnabled")); + ScriptClientConfig.setRunLoadedScriptsFirst(compound.getBoolean("LoadedFirst")); ScriptController.Instance.globalRevision = compound.getInteger("GlobalRevision"); } diff --git a/src/main/java/noppes/npcs/CommonProxy.java b/src/main/java/noppes/npcs/CommonProxy.java index 6fefe01c1..ee65ad6a5 100644 --- a/src/main/java/noppes/npcs/CommonProxy.java +++ b/src/main/java/noppes/npcs/CommonProxy.java @@ -43,6 +43,7 @@ import noppes.npcs.containers.ContainerNpcQuestReward; import noppes.npcs.containers.ContainerNpcQuestTypeItem; import noppes.npcs.controllers.data.AnimationData; import noppes.npcs.controllers.data.PlayerData; +import noppes.npcs.config.ConfigScript; import noppes.npcs.entity.EntityNPCInterface; public class CommonProxy implements IGuiHandler { @@ -220,4 +221,24 @@ public class CommonProxy implements IGuiHandler { public void buildPackageIndex() { } + + public boolean isScriptingEnabled() { + return ConfigScript.ScriptingEnabled; + } + + public boolean isRunLoadedScriptsFirst() { + return ConfigScript.RunLoadedScriptsFirst; + } + + public boolean isGlobalPlayerScripts() { + return ConfigScript.GlobalPlayerScripts; + } + + public boolean isGlobalForgeScripts() { + return ConfigScript.GlobalForgeScripts; + } + + public boolean isGlobalNPCScripts() { + return ConfigScript.GlobalNPCScripts; + } } diff --git a/src/main/java/noppes/npcs/client/ClientEventHandler.java b/src/main/java/noppes/npcs/client/ClientEventHandler.java index 7a839fbb5..a7a0f2b39 100644 --- a/src/main/java/noppes/npcs/client/ClientEventHandler.java +++ b/src/main/java/noppes/npcs/client/ClientEventHandler.java @@ -19,6 +19,8 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderLivingEvent; import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import noppes.npcs.client.gui.player.AuctionTooltipHandler; import noppes.npcs.client.gui.customoverlay.OverlayCustom; import noppes.npcs.client.gui.hud.ClientHudManager; import noppes.npcs.client.gui.hud.CompassHudComponent; @@ -75,6 +77,11 @@ public class ClientEventHandler { } } + @SubscribeEvent + public void onItemTooltip(ItemTooltipEvent event) { + AuctionTooltipHandler.onItemTooltip(event); + } + @SubscribeEvent public void onMouse(MouseEvent event) { if (Minecraft.getMinecraft().currentScreen != null) diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index 94d400317..e3f73ae9b 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -194,6 +194,7 @@ import noppes.npcs.client.renderer.items.ItemShortLampRenderer; import noppes.npcs.client.renderer.items.ItemTallLampRenderer; import noppes.npcs.client.renderer.items.ItemToolRenderer; import noppes.npcs.client.renderer.items.ScriptedBlockItemRenderer; +import noppes.npcs.client.ScriptClientConfig; import noppes.npcs.config.ConfigClient; import noppes.npcs.config.ConfigItem; import noppes.npcs.config.ConfigMain; @@ -828,6 +829,31 @@ public class ClientProxy extends CommonProxy { } } + @Override + public boolean isScriptingEnabled() { + return ConfigClient.AllowClientScripts && ScriptClientConfig.isScriptingEnabled(); + } + + @Override + public boolean isRunLoadedScriptsFirst() { + return ScriptClientConfig.isRunLoadedScriptsFirst(); + } + + @Override + public boolean isGlobalPlayerScripts() { + return ConfigClient.AllowClientScripts && ScriptClientConfig.isGlobalPlayerScripts(); + } + + @Override + public boolean isGlobalForgeScripts() { + return ConfigClient.AllowClientScripts && ScriptClientConfig.isGlobalForgeScripts(); + } + + @Override + public boolean isGlobalNPCScripts() { + return ConfigClient.AllowClientScripts && ScriptClientConfig.isGlobalNPCScripts(); + } + public static class FontContainer { public StringCache textFont = null; public boolean useCustomFont = true; diff --git a/src/main/java/noppes/npcs/client/ScriptClientConfig.java b/src/main/java/noppes/npcs/client/ScriptClientConfig.java new file mode 100644 index 000000000..863f991ba --- /dev/null +++ b/src/main/java/noppes/npcs/client/ScriptClientConfig.java @@ -0,0 +1,61 @@ +package noppes.npcs.client; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Client-side cache for script configuration synced from server. + * Prevents client from using local ConfigScript which may differ from server settings. + * + * IMPORTANT: This class is CLIENT-ONLY. Server-side NBT writing is done in + * ScriptConfigSync to avoid @SideOnly crashes on dedicated servers. + */ +@SideOnly(Side.CLIENT) +public class ScriptClientConfig { + // Cached values from server + private static boolean scriptingEnabled = true; + private static boolean runLoadedScriptsFirst = true; + private static boolean globalPlayerScripts = true; + private static boolean globalForgeScripts = true; + private static boolean globalNPCScripts = false; + + /** + * Update cached config from server NBT data. + * Called when receiving login packet or script sync packets. + */ + public static void readFromNBT(NBTTagCompound compound) { + if (compound == null) return; + + scriptingEnabled = compound.getBoolean("ScriptingEnabled"); + runLoadedScriptsFirst = compound.getBoolean("RunLoadedScriptsFirst"); + globalPlayerScripts = compound.getBoolean("GlobalPlayerScripts"); + globalForgeScripts = compound.getBoolean("GlobalForgeScripts"); + globalNPCScripts = compound.getBoolean("GlobalNPCScripts"); + } + + /** + * Reset to defaults (called on disconnect). + */ + public static void reset() { + scriptingEnabled = true; + runLoadedScriptsFirst = true; + globalPlayerScripts = true; + globalForgeScripts = true; + globalNPCScripts = false; + } + + // Setters for individual packet updates + public static void setScriptingEnabled(boolean value) { scriptingEnabled = value; } + public static void setRunLoadedScriptsFirst(boolean value) { runLoadedScriptsFirst = value; } + public static void setGlobalPlayerScripts(boolean value) { globalPlayerScripts = value; } + public static void setGlobalForgeScripts(boolean value) { globalForgeScripts = value; } + public static void setGlobalNPCScripts(boolean value) { globalNPCScripts = value; } + + // Getters + public static boolean isScriptingEnabled() { return scriptingEnabled; } + public static boolean isRunLoadedScriptsFirst() { return runLoadedScriptsFirst; } + public static boolean isGlobalPlayerScripts() { return globalPlayerScripts; } + public static boolean isGlobalForgeScripts() { return globalForgeScripts; } + public static boolean isGlobalNPCScripts() { return globalNPCScripts; } +} diff --git a/src/main/java/noppes/npcs/client/gui/player/AuctionTooltipHandler.java b/src/main/java/noppes/npcs/client/gui/player/AuctionTooltipHandler.java index a50622529..11ffa198d 100644 --- a/src/main/java/noppes/npcs/client/gui/player/AuctionTooltipHandler.java +++ b/src/main/java/noppes/npcs/client/gui/player/AuctionTooltipHandler.java @@ -4,14 +4,18 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.StatCollector; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; import noppes.npcs.client.AuctionClientConfig; import noppes.npcs.constants.EnumClaimType; import noppes.npcs.containers.ContainerAuctionListing; import noppes.npcs.containers.ContainerAuctionSell; import noppes.npcs.containers.ContainerAuctionTrades; +import noppes.npcs.containers.SlotAuctionDisplay; import noppes.npcs.controllers.data.AuctionClaim; import noppes.npcs.controllers.data.AuctionListing; @@ -19,45 +23,60 @@ import java.util.List; /** * Handles adding auction information to item tooltips when viewing auction GUIs. - * Called from MixinItem when Item.addInformation is invoked. + * Called from ItemTooltipEvent in ClientEventHandler. + * + * Uses slot-based lookup (slot index) instead of ItemStack comparison to correctly + * handle duplicate items with different auction data (e.g., two porkchops at different prices). */ @SideOnly(Side.CLIENT) public class AuctionTooltipHandler { /** - * Add auction information to an item's tooltip if we're in an auction GUI. + * Handle ItemTooltipEvent - append auction info to the end of the tooltip. */ - public static void addAuctionInfo(ItemStack stack, List tooltip) { - if (stack == null || tooltip == null) return; + public static void onItemTooltip(ItemTooltipEvent event) { + if (event.itemStack == null) return; Minecraft mc = Minecraft.getMinecraft(); if (mc == null || mc.currentScreen == null) return; GuiScreen screen = mc.currentScreen; - // Check if we're in an auction listing GUI if (screen instanceof GuiAuctionListing) { - addListingTooltip((GuiAuctionListing) screen, stack, tooltip); - } - // Check if we're in the trades GUI - else if (screen instanceof GuiAuctionTrades) { - addTradesTooltip((GuiAuctionTrades) screen, stack, tooltip); - } - // Check if we're in the sell GUI - else if (screen instanceof GuiAuctionSell) { - addSellTooltip((GuiAuctionSell) screen, stack, tooltip); + addListingTooltip((GuiAuctionListing) screen, event.itemStack, event.toolTip); + } else if (screen instanceof GuiAuctionTrades) { + addTradesTooltip((GuiAuctionTrades) screen, event.itemStack, event.toolTip); + } else if (screen instanceof GuiAuctionSell) { + addSellTooltip((GuiAuctionSell) screen, event.itemStack, event.toolTip); } } /** - * Add auction info when viewing the main listings page. + * Find the slot in the container whose getStack() returns the exact same ItemStack reference. + * This avoids the duplicate item bug where ItemStack.areItemStacksEqual matches the wrong listing. */ + private static Slot findHoveredSlot(GuiContainer gui, ItemStack stack) { + if (stack == null) return null; + for (Object obj : gui.inventorySlots.inventorySlots) { + Slot slot = (Slot) obj; + if (slot.getHasStack() && slot.getStack() == stack) { + return slot; + } + } + return null; + } + + // ========== Listing Page ========== + private static void addListingTooltip(GuiAuctionListing gui, ItemStack stack, List tooltip) { - ContainerAuctionListing container = (ContainerAuctionListing) gui.inventorySlots; + Slot slot = findHoveredSlot(gui, stack); + if (slot == null || !(slot instanceof SlotAuctionDisplay)) return; + + ContainerAuctionListing container = gui.getListingContainer(); if (container == null) return; - // Find the listing that matches this ItemStack - AuctionListing listing = container.getListingForItem(stack); + int displayIndex = slot.getSlotIndex(); + AuctionListing listing = container.getListingAt(displayIndex); if (listing == null) return; tooltip.add(""); @@ -90,149 +109,153 @@ public class AuctionTooltipHandler { tooltip.add((timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE) + timeText); } - /** - * Add auction info when viewing the trades page. - */ + // ========== Trades Page ========== + private static void addTradesTooltip(GuiAuctionTrades gui, ItemStack stack, List tooltip) { - ContainerAuctionTrades container = (ContainerAuctionTrades) gui.inventorySlots; + Slot slot = findHoveredSlot(gui, stack); + if (slot == null || !(slot instanceof SlotAuctionDisplay)) return; + + ContainerAuctionTrades container = gui.getTradesContainer(); if (container == null) return; - // Check if it's an item I'm selling - AuctionListing sellingListing = container.getSellingListingForItem(stack); - if (sellingListing != null) { - tooltip.add(""); - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.selling")); + int displayIndex = slot.getSlotIndex(); - if (sellingListing.hasBids()) { - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.currentBid") - .replace("%s", EnumChatFormatting.GOLD + formatCurrency(sellingListing.currentBid))); - tooltip.add(EnumChatFormatting.GRAY + String.format(StatCollector.translateToLocal("auction.bids"), sellingListing.bidCount)); - } else { - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.noBids")); + // Check listings first (selling or bidding) + AuctionListing listing = container.getListingAt(displayIndex); + if (listing != null) { + if (container.isSellingAt(displayIndex)) { + addSellingTooltip(listing, tooltip); + } else if (container.isBiddingAt(displayIndex)) { + addBiddingTooltip(listing, tooltip); } - - long timeRemaining = sellingListing.getTimeRemaining(); - String timeText = StatCollector.translateToLocal("auction.timeLeft") - .replace("%s", formatTimeRemaining(timeRemaining)); - tooltip.add((timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE) + timeText); - - tooltip.add(""); - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.rightClickToCancel")); return; } - // Check if it's an item I'm bidding on - AuctionListing biddingListing = container.getBiddingListingForItem(stack); - if (biddingListing != null) { - tooltip.add(""); - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.bidding")); - - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.currentBid") - .replace("%s", EnumChatFormatting.GOLD + formatCurrency(biddingListing.currentBid))); - tooltip.add(EnumChatFormatting.GRAY + String.format(StatCollector.translateToLocal("auction.bids"), biddingListing.bidCount)); - - long timeRemaining = biddingListing.getTimeRemaining(); - String timeText = StatCollector.translateToLocal("auction.timeLeft") - .replace("%s", formatTimeRemaining(timeRemaining)); - tooltip.add((timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE) + timeText); - - tooltip.add(""); - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.trades.clickToRebid")); - return; - } - - // Check if it's a claim - AuctionClaim claim = container.getClaimForItem(stack); + // Check claims + AuctionClaim claim = container.getClaimAt(displayIndex); if (claim != null) { - tooltip.add(""); - - // Title based on claim type - switch (claim.type) { - case ITEM: - if (claim.isReturned) { - // Expired - returned to seller - tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.expired")); - } else { - // Won item - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.won")); - } - break; - case CURRENCY: - // Sold - money to claim - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.sold")); - if (claim.itemName != null && !claim.itemName.isEmpty()) { - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.soldItem") - .replace("%s", EnumChatFormatting.WHITE + claim.itemName)); - } - if (claim.otherPlayerName != null && !claim.otherPlayerName.isEmpty()) { - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.buyer") - .replace("%s", EnumChatFormatting.WHITE + claim.otherPlayerName)); - } - break; - case REFUND: - // Outbid - refund available - tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.outbid")); - if (claim.otherPlayerName != null && !claim.otherPlayerName.isEmpty()) { - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.outbidBy") - .replace("%s", EnumChatFormatting.WHITE + claim.otherPlayerName)); - } - break; - } - - // Amount for currency claims (including REFUND) - if (claim.type.isCurrency()) { - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.claim.amount") - .replace("%s", EnumChatFormatting.GOLD + formatCurrency(claim.currency))); - } - - // Expiration - int expirationDays = AuctionClientConfig.getClaimExpirationDays(); - long daysLeft = claim.getDaysUntilExpiration(expirationDays); - if (daysLeft <= 1) { - tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.claim.expiresSoon")); - } else { - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.expires") - .replace("%s", "" + daysLeft)); - } - - tooltip.add(""); - - // Show rebid option for REFUND claims with item (active auction) - if (claim.type == EnumClaimType.REFUND && claim.item != null) { - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.leftClickRebid")); - tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.rightClickRefund")); - } else { - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.claim.clickToClaim")); - } + addClaimTooltip(claim, tooltip); } } - /** - * Add sell page tooltip info. - */ + private static void addSellingTooltip(AuctionListing listing, List tooltip) { + tooltip.add(""); + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.selling")); + + if (listing.hasBids()) { + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.currentBid") + .replace("%s", EnumChatFormatting.GOLD + formatCurrency(listing.currentBid))); + tooltip.add(EnumChatFormatting.GRAY + String.format(StatCollector.translateToLocal("auction.bids"), listing.bidCount)); + } else { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.noBids")); + } + + long timeRemaining = listing.getTimeRemaining(); + String timeText = StatCollector.translateToLocal("auction.timeLeft") + .replace("%s", formatTimeRemaining(timeRemaining)); + tooltip.add((timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE) + timeText); + + tooltip.add(""); + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.rightClickToCancel")); + } + + private static void addBiddingTooltip(AuctionListing listing, List tooltip) { + tooltip.add(""); + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.bidding")); + + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.currentBid") + .replace("%s", EnumChatFormatting.GOLD + formatCurrency(listing.currentBid))); + tooltip.add(EnumChatFormatting.GRAY + String.format(StatCollector.translateToLocal("auction.bids"), listing.bidCount)); + + long timeRemaining = listing.getTimeRemaining(); + String timeText = StatCollector.translateToLocal("auction.timeLeft") + .replace("%s", formatTimeRemaining(timeRemaining)); + tooltip.add((timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE) + timeText); + + tooltip.add(""); + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.trades.clickToRebid")); + } + + private static void addClaimTooltip(AuctionClaim claim, List tooltip) { + tooltip.add(""); + + switch (claim.type) { + case ITEM: + if (claim.isReturned) { + tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.expired")); + } else { + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.won")); + } + break; + case CURRENCY: + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.sold")); + if (claim.itemName != null && !claim.itemName.isEmpty()) { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.soldItem") + .replace("%s", EnumChatFormatting.WHITE + claim.itemName)); + } + if (claim.otherPlayerName != null && !claim.otherPlayerName.isEmpty()) { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.buyer") + .replace("%s", EnumChatFormatting.WHITE + claim.otherPlayerName)); + } + break; + case REFUND: + tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.outbid")); + if (claim.otherPlayerName != null && !claim.otherPlayerName.isEmpty()) { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.outbidBy") + .replace("%s", EnumChatFormatting.WHITE + claim.otherPlayerName)); + } + break; + } + + // Amount for currency claims + if (claim.type.isCurrency()) { + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.claim.amount") + .replace("%s", EnumChatFormatting.GOLD + formatCurrency(claim.currency))); + } + + // Expiration + int expirationDays = AuctionClientConfig.getClaimExpirationDays(); + long daysLeft = claim.getDaysUntilExpiration(expirationDays); + if (daysLeft <= 1) { + tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.claim.expiresSoon")); + } else { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.claim.expires") + .replace("%s", "" + daysLeft)); + } + + tooltip.add(""); + + // Rebid option for REFUND claims with item + if (claim.type == EnumClaimType.REFUND && claim.item != null) { + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.leftClickRebid")); + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.rightClickRefund")); + } else { + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.claim.clickToClaim")); + } + } + + // ========== Sell Page ========== + private static void addSellTooltip(GuiAuctionSell gui, ItemStack stack, List tooltip) { + Slot slot = findHoveredSlot(gui, stack); + if (slot == null) return; + ContainerAuctionSell container = (ContainerAuctionSell) gui.inventorySlots; if (container == null) return; - // Check if this is the sell slot item - ItemStack sellItem = container.getItemToSell(); - if (sellItem != null && ItemStack.areItemStacksEqual(stack, sellItem)) { - // This is the item in the sell slot + if (container.isSellSlot(slot.slotNumber)) { tooltip.add(""); tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.sell.leftClear")); tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.sell.rightRemove")); - return; + } else if (container.isPlayerInventorySlot(slot.slotNumber)) { + tooltip.add(""); + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.sell.leftAdd")); + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.sell.rightAddOne")); } - - // This is an inventory item - show add controls - tooltip.add(""); - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.sell.leftAdd")); - tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.sell.rightAddOne")); } - /** - * Format currency with commas. - */ + // ========== Formatting Utilities ========== + private static String formatCurrency(long amount) { if (amount < 1000) { return amount + " " + AuctionClientConfig.getCurrencyName(); @@ -250,9 +273,6 @@ public class AuctionTooltipHandler { return sb.toString() + " " + AuctionClientConfig.getCurrencyName(); } - /** - * Format time remaining. - */ private static String formatTimeRemaining(long milliseconds) { if (milliseconds <= 0) { return StatCollector.translateToLocal("auction.ended"); diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionInterface.java b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionInterface.java index 7efdbe9c9..c34223550 100644 --- a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionInterface.java +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionInterface.java @@ -112,16 +112,16 @@ public abstract class GuiAuctionInterface extends GuiContainerNPCInterface { mc.renderEngine.bindTexture(AUCTION_BACKGROUND); drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + drawAuctionContent(partialTicks, mouseX, mouseY); + RenderHelper.enableGUIStandardItemLighting(); GL11.glEnable(GL12.GL_RESCALE_NORMAL); GL11.glEnable(GL11.GL_COLOR_MATERIAL); - GL11.glEnable(GL11.GL_LIGHTING); - - drawAuctionContent(partialTicks, mouseX, mouseY); - GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_DEPTH_TEST); - RenderHelper.enableStandardItemLighting(); super.drawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY); } @@ -167,12 +167,12 @@ public abstract class GuiAuctionInterface extends GuiContainerNPCInterface { Gui.drawRect(x + 1, y + 1, x + 17, y + 17, color); GL11.glDisable(GL11.GL_BLEND); GL11.glEnable(GL11.GL_DEPTH_TEST); - GL11.glEnable(GL11.GL_LIGHTING); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); } /** Draw dark overlay for unavailable slots */ protected void drawDarkenedOverlay(int x, int y) { - drawColoredOverlay(x, y, 0x80000000); + drawColoredOverlay(x, y, 0xC0000000); } /** Draw icon texture on slot (16x16) */ diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java index 21b0f6f01..f52d5fa63 100644 --- a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java @@ -38,10 +38,10 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { private static final int TOTAL_SLOTS = COLS * ROWS; // Slot tint colors (ARGB) - private static final int TINT_BLUE = 0x303060FF; // Active listing/bid (selling) - private static final int TINT_GREEN = 0x3030FF30; // Sold (currency claim) or Won (item claim) - private static final int TINT_YELLOW = 0x30FFFF30; // Outbid (refund claim) - private static final int TINT_RED = 0x30FF3030; // Expired/Returned (item returned to seller) + private static final int TINT_BLUE = 0x605080FF; // Active listing/bid (selling) + private static final int TINT_GREEN = 0x6050FF50; // Sold (currency claim) or Won (item claim) + private static final int TINT_YELLOW = 0x60FFFF50; // Outbid (refund claim) + private static final int TINT_RED = 0x60FF5050; // Expired/Returned (item returned to seller) private final ContainerAuctionTrades tradesContainer; private int maxTradeSlots; diff --git a/src/main/java/noppes/npcs/config/ConfigClient.java b/src/main/java/noppes/npcs/config/ConfigClient.java index c24a2e301..d8c093382 100644 --- a/src/main/java/noppes/npcs/config/ConfigClient.java +++ b/src/main/java/noppes/npcs/config/ConfigClient.java @@ -22,6 +22,9 @@ public class ConfigClient { /** * General Properties **/ + public static Property AllowClientScriptsProperty; + public static boolean AllowClientScripts = true; + public static Property CacheLifeProperty; public static int CacheLife = 10; @@ -185,6 +188,9 @@ public class ConfigClient { // General + AllowClientScriptsProperty = config.get(GENERAL, "Allow Client Scripts", true, "Allow the server to run scripts on the client. If disabled, no server scripts will execute client-side."); + AllowClientScripts = AllowClientScriptsProperty.getBoolean(true); + CacheLifeProperty = config.get(GENERAL, "Cache Life", 10, "How long should downloaded imagery data be saved client side? (In minutes)"); CacheLife = CacheLifeProperty.getInt(10); diff --git a/src/main/java/noppes/npcs/controllers/ScriptConfigSync.java b/src/main/java/noppes/npcs/controllers/ScriptConfigSync.java new file mode 100644 index 000000000..5a9d32ed3 --- /dev/null +++ b/src/main/java/noppes/npcs/controllers/ScriptConfigSync.java @@ -0,0 +1,25 @@ +package noppes.npcs.controllers; + +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.config.ConfigScript; + +/** + * Server-side utility for syncing script configuration to clients. + * + * This class is SERVER-SIDE ONLY. Client reads via ScriptClientConfig. + */ +public class ScriptConfigSync { + + /** + * Write script config to NBT for sending to client. + * Called on server side when building login packet. + */ + public static NBTTagCompound writeToNBT(NBTTagCompound compound) { + compound.setBoolean("ScriptingEnabled", ConfigScript.ScriptingEnabled); + compound.setBoolean("RunLoadedScriptsFirst", ConfigScript.RunLoadedScriptsFirst); + compound.setBoolean("GlobalPlayerScripts", ConfigScript.GlobalPlayerScripts); + compound.setBoolean("GlobalForgeScripts", ConfigScript.GlobalForgeScripts); + compound.setBoolean("GlobalNPCScripts", ConfigScript.GlobalNPCScripts); + return compound; + } +} diff --git a/src/main/java/noppes/npcs/controllers/ScriptContainer.java b/src/main/java/noppes/npcs/controllers/ScriptContainer.java index ad2ca2f1f..dbeddf407 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptContainer.java +++ b/src/main/java/noppes/npcs/controllers/ScriptContainer.java @@ -3,6 +3,7 @@ package noppes.npcs.controllers; import cpw.mods.fml.common.eventhandler.Event; import jdk.nashorn.api.scripting.ScriptObjectMirror; import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.CustomNpcs; import noppes.npcs.NBTTags; import noppes.npcs.config.ConfigScript; import noppes.npcs.constants.EnumScriptType; @@ -104,7 +105,7 @@ public class ScriptContainer implements IScriptUnit { if (!this.evaluated) { // build includes first depending on config setting StringBuilder sb = new StringBuilder(); - if (ConfigScript.RunLoadedScriptsFirst) { + if (CustomNpcs.proxy.isRunLoadedScriptsFirst()) { this.appendExternalScripts(sb); } @@ -114,7 +115,7 @@ public class ScriptContainer implements IScriptUnit { } // build includes last if not built first - if (!ConfigScript.RunLoadedScriptsFirst) { + if (!CustomNpcs.proxy.isRunLoadedScriptsFirst()) { this.appendExternalScripts(sb); } this.fullscript = sb.toString(); @@ -158,14 +159,14 @@ public class ScriptContainer implements IScriptUnit { } public void run(EnumScriptType type, Event event) { - if (!ConfigScript.ScriptingEnabled) + if (!CustomNpcs.proxy.isScriptingEnabled()) return; this.run((String) type.function, (Object) event); } public void run(String type, Object event) { - if (!ConfigScript.ScriptingEnabled || errored || !hasCode() || unknownFunctions.contains(type)) + if (!CustomNpcs.proxy.isScriptingEnabled() || errored || !hasCode() || unknownFunctions.contains(type)) return; this.setEngine(handler.getLanguage()); diff --git a/src/main/java/noppes/npcs/controllers/ScriptController.java b/src/main/java/noppes/npcs/controllers/ScriptController.java index af2153cff..546b3680d 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptController.java +++ b/src/main/java/noppes/npcs/controllers/ScriptController.java @@ -73,7 +73,7 @@ public class ScriptController { public ScriptController() { Instance = this; manager = new ScriptEngineManager(); - if (!ConfigScript.ScriptingEnabled) + if (!CustomNpcs.proxy.isScriptingEnabled()) return; LogWriter.info("Script Engines Available:"); diff --git a/src/main/java/noppes/npcs/controllers/data/DataScript.java b/src/main/java/noppes/npcs/controllers/data/DataScript.java index 78026b329..7d9fb5c3a 100644 --- a/src/main/java/noppes/npcs/controllers/data/DataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/DataScript.java @@ -9,13 +9,13 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; import net.minecraft.world.WorldServer; +import noppes.npcs.CustomNpcs; import noppes.npcs.EventHooks; import noppes.npcs.LogWriter; import noppes.npcs.NBTTags; import noppes.npcs.api.IWorld; import noppes.npcs.api.entity.ICustomNpc; import noppes.npcs.config.ConfigDebug; -import noppes.npcs.config.ConfigScript; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; @@ -207,7 +207,7 @@ public class DataScript implements IScriptHandlerPacket { } public boolean isEnabled() { - return enabled && ScriptController.HasStart && !npc.worldObj.isRemote && !scripts.isEmpty() && ConfigScript.ScriptingEnabled; + return enabled && ScriptController.HasStart && !npc.worldObj.isRemote && !scripts.isEmpty() && CustomNpcs.proxy.isScriptingEnabled(); } @Override diff --git a/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java b/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java index 1c6f09da7..f8c961db7 100644 --- a/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java @@ -12,8 +12,8 @@ import net.minecraftforge.event.world.ChunkDataEvent; import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.ChunkWatchEvent; import net.minecraftforge.event.world.WorldEvent; +import noppes.npcs.CustomNpcs; import noppes.npcs.EventHooks; -import noppes.npcs.config.ConfigScript; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; @@ -36,7 +36,7 @@ public class ForgeDataScript extends MultiScriptHandler { } public boolean isEnabled() { - return this.enabled && ConfigScript.GlobalForgeScripts && ScriptController.HasStart && this.scripts.size() > 0; + return this.enabled && CustomNpcs.proxy.isGlobalForgeScripts() && ScriptController.HasStart && this.scripts.size() > 0; } @Override diff --git a/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java b/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java index 805ae7c8d..4654ff68f 100644 --- a/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java @@ -3,8 +3,8 @@ package noppes.npcs.controllers.data; import kamkeel.npcs.network.packets.request.script.GlobalNPCScriptPacket; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.math.BlockPos; +import noppes.npcs.CustomNpcs; import noppes.npcs.api.entity.ICustomNpc; -import noppes.npcs.config.ConfigScript; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptController; import noppes.npcs.entity.EntityNPCInterface; @@ -28,7 +28,7 @@ public class GlobalNPCDataScript extends MultiScriptHandler { } public boolean isEnabled() { - return ConfigScript.GlobalNPCScripts && this.enabled && ScriptController.HasStart && this.scripts.size() > 0; + return CustomNpcs.proxy.isGlobalNPCScripts() && this.enabled && ScriptController.HasStart && this.scripts.size() > 0; } @Override diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java index 38d9e8447..95f8bcb8e 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java @@ -5,6 +5,7 @@ import kamkeel.npcs.network.packets.request.script.PlayerScriptPacket; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.math.BlockPos; +import noppes.npcs.CustomNpcs; import noppes.npcs.EventHooks; import noppes.npcs.NBTTags; import noppes.npcs.api.entity.IPlayer; @@ -60,7 +61,7 @@ public class PlayerDataScript extends MultiScriptHandler { } public boolean isEnabled() { - return ConfigScript.GlobalPlayerScripts && ScriptController.Instance.playerScripts.enabled && ScriptController.HasStart && (this.player == null || !this.player.worldObj.isRemote); + return CustomNpcs.proxy.isGlobalPlayerScripts() && ScriptController.Instance.playerScripts.enabled && ScriptController.HasStart && (this.player == null || !this.player.worldObj.isRemote); } @Override diff --git a/src/main/java/noppes/npcs/controllers/data/ScriptHandler.java b/src/main/java/noppes/npcs/controllers/data/ScriptHandler.java index 684c9a45b..096f8f381 100644 --- a/src/main/java/noppes/npcs/controllers/data/ScriptHandler.java +++ b/src/main/java/noppes/npcs/controllers/data/ScriptHandler.java @@ -1,7 +1,7 @@ package noppes.npcs.controllers.data; import cpw.mods.fml.common.eventhandler.Event; -import noppes.npcs.config.ConfigScript; +import noppes.npcs.CustomNpcs; import noppes.npcs.controllers.ScriptController; import java.util.ArrayList; @@ -14,7 +14,7 @@ public abstract class ScriptHandler implements IScriptHandler { protected long lastInited = -1; protected boolean canRunScripts() { - return enabled && ScriptController.HasStart && ConfigScript.ScriptingEnabled && scripts != null && !scripts.isEmpty(); + return enabled && ScriptController.HasStart && CustomNpcs.proxy.isScriptingEnabled() && scripts != null && !scripts.isEmpty(); } @Override diff --git a/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java b/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java index 8b30ee3ae..854e77941 100644 --- a/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java +++ b/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java @@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf; import kamkeel.npcs.util.ByteBufUtils; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.Constants; -import noppes.npcs.config.ConfigScript; +import noppes.npcs.CustomNpcs; import noppes.npcs.controllers.ScriptController; import java.io.IOException; @@ -32,7 +32,7 @@ public abstract class SingleScriptHandler extends ScriptHandler { } protected boolean canRunScripts() { - return enabled && ScriptController.HasStart && ConfigScript.ScriptingEnabled && container != null; + return enabled && ScriptController.HasStart && CustomNpcs.proxy.isScriptingEnabled() && container != null; } @Override diff --git a/src/main/java/noppes/npcs/janino/JaninoScript.java b/src/main/java/noppes/npcs/janino/JaninoScript.java index 337cc9ae8..0014bccea 100644 --- a/src/main/java/noppes/npcs/janino/JaninoScript.java +++ b/src/main/java/noppes/npcs/janino/JaninoScript.java @@ -7,7 +7,6 @@ import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.CustomNpcs; import noppes.npcs.NBTTags; import noppes.npcs.api.handler.IHookDefinition; -import noppes.npcs.config.ConfigScript; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptController; @@ -154,9 +153,9 @@ public abstract class JaninoScript implements IScriptUnit { private String getFullCode() { StringBuilder sb = new StringBuilder(); - if (ConfigScript.RunLoadedScriptsFirst) appendExternalScripts(sb); + if (CustomNpcs.proxy.isRunLoadedScriptsFirst()) appendExternalScripts(sb); if (script != null && !script.isEmpty()) sb.append(script).append("\n"); - if (!ConfigScript.RunLoadedScriptsFirst) appendExternalScripts(sb); + if (!CustomNpcs.proxy.isRunLoadedScriptsFirst()) appendExternalScripts(sb); return sb.toString(); } diff --git a/src/main/java/noppes/npcs/mixin/CustomNPCsMixinPlugin.java b/src/main/java/noppes/npcs/mixin/CustomNPCsMixinPlugin.java index c997729ca..9b09759c0 100644 --- a/src/main/java/noppes/npcs/mixin/CustomNPCsMixinPlugin.java +++ b/src/main/java/noppes/npcs/mixin/CustomNPCsMixinPlugin.java @@ -56,7 +56,6 @@ public class CustomNPCsMixinPlugin implements IMixinConfigPlugin { } mixins.add("MixinItemStack"); - mixins.add("MixinItem"); } return mixins; } diff --git a/src/main/java/noppes/npcs/mixin/MixinItem.java b/src/main/java/noppes/npcs/mixin/MixinItem.java deleted file mode 100644 index 638b515ae..000000000 --- a/src/main/java/noppes/npcs/mixin/MixinItem.java +++ /dev/null @@ -1,27 +0,0 @@ -package noppes.npcs.mixin; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import noppes.npcs.client.gui.player.AuctionTooltipHandler; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; - -/** - * Mixin to inject auction information into item tooltips when viewing auction GUIs. - */ -@Mixin(Item.class) -public abstract class MixinItem { - - @SideOnly(Side.CLIENT) - @Inject(method = "addInformation", at = @At("HEAD")) - public void onAddInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced, CallbackInfo ci) { - AuctionTooltipHandler.addAuctionInfo(stack, tooltip); - } -}