# MosswartMassacre — DECAL Plugin for Asheron's Call

> **Status**: Production · In-game companion plugin for the [Mosswart Overlord](https://github.com/SawatoMosswartsEnjoyersClub/MosswartOverlord) backend

A comprehensive DECAL plugin for Asheron's Call that streams live telemetry, vitals, inventory, combat, chat, and rare discoveries to a backend server via WebSocket. Pairs with the Mosswart Overlord web dashboard for real-time multi-account tracking, analytics, and cross-machine utilities.

---

## 📋 Table of Contents
- [Features](#-features)
- [Architecture](#️-architecture)
- [Installation](#-installation)
- [Configuration](#-configuration)
- [In-Game Commands](#-in-game-commands)
- [Event Streams](#-event-streams)
- [Cross-Machine Vital Sharing](#-cross-machine-vital-sharing)
- [Building](#️-building)
- [Technical Details](#-technical-details)
- [Project Structure](#-project-structure)
- [Contributing](#-contributing)

---

## 🚀 Features

### Core Tracking
- **Telemetry**: Position, kills, kills/hour, online time, VTank state, memory/CPU stats — streamed every 2 seconds
- **Vitals**: Health / Stamina / Mana / Vitae percentages, updated continuously
- **Character Stats**: Full attributes, skills, allegiance, augmentations — sent every 10 minutes
- **Chat**: All chat lines forwarded with color preservation (supports server-side filtering by channel)
- **Rare Discoveries**: Automatic detection + classification (common vs great rares)
- **Quest Timers**: Progress tracking with countdown/ready state

### Inventory
- **Full Inventory Dump**: Complete snapshot on login via `full_inventory`
- **Live Deltas**: `inventory_delta` events for add/update/remove on `OnCreate`, `OnChange`, `OnRelease`
- **ID Requests**: Auto-requests appraisal for items needing spell/combat data
- **Rich Metadata**: Spells, materials, tinkers, workmanship, mana charge, imbues, spellcraft

### Combat Stats (Mag-Tools Style)
- **CombatStatsTracker**: Parses ~50 chat regex patterns matching Mag-Tools combat output
- **Per-element Breakdown**: Damage offense/defense split by slashing/piercing/bludgeoning/fire/cold/acid/lightning
- **Monster Records**: Per-monster damage given/received/kill count
- **Aetheria Surge Tracking**: Counts surge triggers
- **Session Deltas**: Sends session snapshot every 10s; backend accumulates lifetime

### Cross-Machine Vital Sharing (VTankFellowHeals Replacement)
- Broadcasts own vitals + debuff state via WebSocket at 150ms intervals
- Receives peer vitals and feeds them into `UBHelper.vTank.Instance`, making fellow members visible to VTank's heal logic across different machines (vs UB's localhost-only default)
- **DxHud Overlay**: In-game UB-style vital bars with direction arrows, Ctrl+drag repositioning

### Nearby Objects / Radar
- On-demand broadcast of nearby monsters, players, NPCs, containers
- Dungeon tile data for the browser's radar overlay
- Range + entity-type filters configurable from backend command

### Navigation Visualization
- Parses VTank `.nav` files (Circular, Linear, Target, Once; Point/Portal/Recall/Pause/ChatCommand/OpenVendor/UseNPC/Checkpoint/Jump waypoints)
- Renders routes as red lines in the 3D game world
- Side-by-side comparison with UtilityBelt's active navigation
- Performance-capped at 500 rendered segments

### Remote Command Execution
- Backend can send `{player_name, command}` envelopes via the `/ws/live` route → plugin executes in-game
- Supports `/radar`, `/mm` subcommands, chat sends, and any `/` command that VTank/game accepts

### Death & Idle Detection (backend-assisted)
- Plugin reports vitals; backend detects vitae 0→>0 transitions (death) and continuous idle state (5-min grace period) and posts to Discord

### Rare Meta Auto-State
- Optional: toggles VTank meta state on rare detection (e.g., to auto-loot or pause)

## 🏗️ Architecture

```
┌─────────────────────────────────────────────────┐
│ Asheron's Call client                          │
│  ┌────────────────────────────────────────────┐ │
│  │ DECAL                                     │ │
│  │  ┌──────────────────────────────────────┐ │ │
│  │  │ MosswartMassacre.dll                │ │ │
│  │  │                                      │ │ │
│  │  │  • PluginCore — lifecycle + wiring  │ │ │
│  │  │  • WebSocket client                 │ │ │
│  │  │  • KillTracker / CombatStatsTracker │ │ │
│  │  │  • LiveInventoryTracker             │ │ │
│  │  │  • VitalSharingTracker              │ │ │
│  │  │  • VitalSharingOverlayView (DxHud)  │ │ │
│  │  │  • NavVisualization                 │ │ │
│  │  │  • VVSTabbedMainView (UI)           │ │ │
│  │  └──────────────────┬───────────────────┘ │ │
│  └─────────────────────│────────────────────── │
└─────────────────────────│─────────────────────┘
                          │ wss://
                          ▼
           overlord.snakedesert.se/ws/position
           (Mosswart Overlord backend)
```

DECAL COM objects are **apartment-threaded (STA)** — the plugin carefully uses `System.Windows.Forms.Timer` (never `System.Timers.Timer`) for any periodic work touching the game API, and hooks `EchoFilter.ServerDispatch` early enough to catch pre-login events.

## 📥 Installation

### Prerequisites
- Windows with .NET Framework 4.8
- Asheron's Call client with DECAL Adapter installed
- VirindiViewService (included in `lib/`)

### Quick Setup
1. Build (or grab the release DLL): see [Building](#️-building)
2. Copy `MosswartMassacre.dll` to `C:\Games\Decal Plugins\MosswartMassacre\`
3. Restart DECAL and enable the plugin under *Plugins → MosswartMassacre*
4. Configure settings through the tabbed in-game UI

### Building from Source

```bash
# From repo root
dotnet build mossy.sln -c Release -v minimal
```

Output DLL: `MosswartMassacre\MosswartMassacre\bin\Release\MosswartMassacre.dll`

## ⚙️ Configuration

Settings are stored per-character in YAML at `<PluginDir>\<CharacterName>.yaml`:

```yaml
# Core
websocket_enabled: true
telemetry_enabled: true
char_tag: "default"
rare_meta_enabled: true
remote_commands_enabled: true
http_server_enabled: false

# Vital Sharing
vital_sharing_enabled: true
vital_sharing_overlay_x: 100
vital_sharing_overlay_y: 200

# Navigation
vtank_profiles_path: "C:\\Games\\VirindiPlugins\\VirindiTank\\"

# UI
main_window_x: 100
main_window_y: 100
```

### Backend endpoint

The WebSocket URL is set in `WebSocket.cs`. Shared-secret auth is sent as a query string parameter.

## 🎮 In-Game Commands

Access plugin commands via `/mm`:

```
/mm help                    — show all commands
/mm report                  — current kill stats
/mm loc                     — current map coordinates
/mm reset                   — reset counters/timers
/mm meta                    — toggle auto rare meta state
/mm start_radar             — start broadcasting nearby objects
/mm stop_radar              — stop radar broadcasts
/mm http <on|off>           — toggle local HTTP command server (port 8085)
/mm telemetry <on|off>      — toggle telemetry streaming
/mm vitalsharing <on|off>   — toggle cross-machine vital sharing
/mm combat <show|reset>     — combat stats session control
```

## 📡 Event Streams

All events are JSON frames over a single `/ws/position` WebSocket connection. Type discriminator is the `type` field.

| Event | Frequency | Purpose |
|---|---|---|
| `telemetry` | 2s | Position, kill count, session metrics |
| `vitals` | 150ms (on change) | HP / Stam / Mana / Vitae |
| `character_stats` | 10 min | Full attributes/skills |
| `full_inventory` | login | Complete inventory snapshot |
| `inventory_delta` | on item add/change/remove | Incremental per-item update |
| `equipment_cantrip_state` | on equip change | Current equipped spell effects |
| `combat_stats` | 10s | Mag-Tools-style combat deltas |
| `chat` | on line | Any chat message |
| `rare` | on discovery | Rare item find |
| `spawn` | on spawn observation | Monster appeared nearby |
| `portal` | on discovery | Portal with coordinates |
| `quest` | on update | Quest timer/progress |
| `nearby_objects` | on radar command | Entities within range |
| `share_vitals` / `share_debuffs` / `share_cast` | continuous | Cross-machine vital sharing |
| `dungeon_map` | on dungeon entry | Floor tile data |

See backend `EVENT_FORMATS.json` for exact schemas.

## 🩺 Cross-Machine Vital Sharing

Default UtilityBelt `VTankFellowHeals` only works across characters on the **same machine** (IPC-based). This plugin replaces it with a WebSocket relay through the backend, enabling multi-box setups across multiple machines.

### How it works

1. Each plugin instance publishes its own `vitals` / cast state via WebSocket at 150ms intervals
2. The backend relays `share_*` envelopes to every subscribed plugin
3. `VitalSharingTracker` feeds peer data into `UBHelper.vTank.Instance` — VTank then behaves as if the peers are on the same machine
4. Hooks packet `0xF7B1` and `0x004A` for cast-attempt detection, plus chat regex for cast success

### DxHud Overlay

A draggable in-game overlay shows each peer's vital bars with a direction arrow pointing toward that player. Ctrl+drag to reposition; click the × to hide.

## 🛠️ Building

### Standard build

```bash
dotnet build mossy.sln -c Release -v minimal
```

### Deploy (local test)

```bash
cp MosswartMassacre\MosswartMassacre\bin\Release\MosswartMassacre.dll \
   "C:\Games\Decal Plugins\MosswartMassacre\MosswartMassacre.dll"
```

Some repo workflows keep the release DLL checked in. If the path is gitignored, use `git add -f` when publishing a release.

### Hot Reload

PluginCore supports an `InitializeForHotReload` path that re-wires trackers without a full DECAL restart. Useful during development — modify, rebuild, replace DLL, invoke hot-reload.

## 🔧 Technical Details

### Build Configuration
- **Target**: .NET Framework 4.8
- **Platform**: AnyCPU / x86 (depending on DECAL host)
- **Architecture**: Direct VirindiViewService integration (no wrapper abstraction)
- **Unsafe blocks**: Enabled for P/Invoke

### Threading Rules
- **Never** touch DECAL COM objects from background threads — they're STA
- Use `System.Windows.Forms.Timer` (UI-thread marshaled), not `System.Timers.Timer`
- WebSocket send methods (`SendXxxAsync`) marshal to a background send queue safely

### Key Files
| File | Responsibility |
|---|---|
| `PluginCore.cs` | Lifecycle, tracker wiring, hot-reload entrypoint |
| `PluginSettings.cs` | YAML per-character config |
| `WebSocket.cs` | Single WS connection, reconnect, send helpers |
| `ChatEventRouter.cs` | Dispatches chat to KillTracker + CombatStatsTracker + others |
| `KillTracker.cs` | Parses kill messages, tracks session kills |
| `CombatStatsTracker.cs` | Parses ~50 regex patterns (Mag-Tools style) |
| `LiveInventoryTracker.cs` | OnCreate/OnChange/OnRelease → inventory_delta |
| `VitalSharingTracker.cs` | Cross-machine vital/debuff relay |
| `VitalSharingOverlayView.cs` | DxHud peer-vitals overlay |
| `MossyInventory.cs` | Rare detection + classification |
| `NavRoute.cs` / `NavVisualization.cs` | VTank .nav parser + renderer |
| `VtankControl.cs` | VTank automation interface |
| `Views/VVSTabbedMainView.cs` | Main tabbed UI |

### Dependencies
- **DECAL Framework** — Decal.Adapter, Decal.Interop.Core, Decal.Interop.D3DService
- **VirindiViewService** — UI framework for game overlays
- **Newtonsoft.Json** — JSON serialization
- **YamlDotNet** — Config file management
- **Mag.Shared** — shared Mag-Tools utilities
- **UBHelper / UtilityBelt interop** — for VTank state injection

## 📁 Project Structure

```
MosswartMassacre/
├── MosswartMassacre/
│   ├── PluginCore.cs
│   ├── PluginSettings.cs
│   ├── WebSocket.cs
│   ├── ChatEventRouter.cs
│   ├── KillTracker.cs
│   ├── CombatStatsTracker.cs
│   ├── CombatInfo.cs
│   ├── LiveInventoryTracker.cs
│   ├── VitalSharingTracker.cs
│   ├── MossyInventory.cs
│   ├── NavRoute.cs
│   ├── NavVisualization.cs
│   ├── VtankControl.cs
│   ├── HttpCommandServer.cs
│   ├── Telemetry.cs
│   ├── Utils.cs
│   ├── Views/
│   │   ├── VVSBaseView.cs
│   │   ├── VVSTabbedMainView.cs
│   │   └── VitalSharingOverlayView.cs
│   ├── ViewXML/
│   │   └── mainViewTabbed.xml
│   └── lib/                 # DECAL / VVS / UB interop DLLs
├── Shared/
│   ├── MyWorldObject.cs
│   └── MyWorldObjectCreator.cs
├── mossy.sln
└── README.md
```

## 🤝 Contributing

1. Fork & branch (`feature/...` or `fix/...`)
2. Respect the DECAL STA threading rules
3. When changing event payload schema, **update both plugin and backend** in the same change — the systems are tightly coupled. See `MosswartOverlord/EVENT_FORMATS.json` for the authoritative contract.
4. Prefer additive payload changes (new optional fields) over renames/removes
5. Test end-to-end: plugin → backend WebSocket → browser dashboard

For cross-repo workflow guidance, see `CLAUDE.md` in both repos.

## 📚 Related

- [MosswartOverlord](https://github.com/SawatoMosswartsEnjoyersClub/MosswartOverlord) — the backend + frontend this plugin feeds
- `CLAUDE.md` — developer guidance (AI assistant context)

## 📄 License

MIT — see `LICENSE`.

---

*Built for the Asheron's Call community.*
